@amritk/nish-x86_64-linux 0.24.0 → 0.25.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 (42) hide show
  1. package/bin/nish +0 -0
  2. package/docs/AI.md +24 -4
  3. package/package.json +1 -1
  4. package/runtime/browser.mjs +4 -3
  5. package/runtime/core/host.d.ts +8 -0
  6. package/runtime/core/host.mjs +50 -2
  7. package/runtime/nish.d.ts +100 -0
  8. package/runtime/nish.h +34 -0
  9. package/runtime/nish.mjs +21 -0
  10. package/runtime/node.mjs +57 -6
  11. package/runtime/runtime-net.c +44 -3
  12. package/runtime/runtime-os.c +115 -12
  13. package/scripts/build.sh +13 -3
  14. package/std/README.md +25 -8
  15. package/std/cli-args.ts +1216 -0
  16. package/std/cli-flags.ts +585 -0
  17. package/std/cli.ts +308 -19
  18. package/std/compress/checksum.ts +132 -20
  19. package/std/compress/deflate.ts +638 -248
  20. package/std/compress/gzip.ts +20 -6
  21. package/std/crypto/LICENSE-fiat-crypto-p384 +21 -0
  22. package/std/crypto/p256.ts +63 -1
  23. package/std/crypto/p384.ts +6044 -0
  24. package/std/crypto/pki-roots.ts +322 -0
  25. package/std/crypto/pki.ts +1207 -0
  26. package/std/crypto/rsa.ts +663 -0
  27. package/std/crypto/x509.ts +1054 -58
  28. package/std/net/dns-config.ts +734 -0
  29. package/std/net/dns-message.ts +517 -0
  30. package/std/net/dns-resolver.ts +1015 -0
  31. package/std/net/dns.ts +478 -0
  32. package/std/net/http1-client.ts +1227 -0
  33. package/std/net/http1-server.ts +7 -2
  34. package/std/net/http1.ts +9 -9
  35. package/std/net/tls/client.ts +1094 -0
  36. package/std/net/tls/codec.ts +585 -3
  37. package/std/net/tls/record-client.ts +1192 -0
  38. package/std/net/tls/verify.ts +221 -0
  39. package/std/net/tls-tcp.ts +642 -11
  40. package/std/net/tls.ts +2 -2
  41. package/std/net/url.ts +559 -4
  42. package/std/yaml-write.ts +415 -0
@@ -0,0 +1,1192 @@
1
+ /**
2
+ * `nish/net/tls/record-client` — a TLS 1.3 client over a byte stream: the
3
+ * record layer of `nish/net/tls/record` wrapped around
4
+ * `nish/net/tls/client`'s `TlsClient`, sans-IO. Bytes the server sent go in,
5
+ * bytes to send come out, and once the handshake is done application data
6
+ * flows both ways. The mirror of `nish/net/tls/record-server`.
7
+ *
8
+ * const conn = new TlsRecordClient(new TlsClient(config, random, x25519Key, p256Key));
9
+ * // send conn.output[conn.outputStart .. conn.outputEnd), then conn.consume(sent)
10
+ * conn.receive(bytes, 0, n); // what the socket read
11
+ * if ((conn.interest() & TLS_RECORD_AUTH) !== 0) {
12
+ * // check conn.tls.peerCertificates() and the CertificateVerify
13
+ * conn.authenticate(verified);
14
+ * }
15
+ * const got: i32 = conn.read(buf, 0, buf.length); // application data
16
+ *
17
+ * **What it adds to `TlsClient`.** Records around the handshake messages:
18
+ * the ClientHello in the clear, then the client's Finished under the
19
+ * handshake keys, and every key installed at the record boundary RFC 8446
20
+ * §5.1 puts it on. The middlebox-compatibility `change_cipher_spec` of §D.4,
21
+ * which a client that sends a session id (this one always does) owes: one,
22
+ * in the clear, before its second ClientHello after a HelloRetryRequest, or
23
+ * else before its first protected record; the server's is dropped from its
24
+ * first handshake record until its Finished. Alerts (§6) both ways, as the
25
+ * server has them: `close_notify` ends the server's stream, `user_canceled`
26
+ * is passed over, any other alert ends the connection, and a refusal of this
27
+ * module's or of `TlsClient`'s is sent as a fatal alert under whatever keys
28
+ * the client writes with. KeyUpdate (§4.6.3) both ways, with the server's
29
+ * limits: `TLS_RECORD_IDLE_LIMIT` records in a row that carry nothing, and
30
+ * `TLS_RECORD_MAX_KEY_UPDATES` KeyUpdates. A NewSessionTicket is handed to
31
+ * `TlsClient`, which checks and drops it; any other post-handshake message is
32
+ * `unexpected_message` (no client certificate is offered, so no
33
+ * CertificateRequest). A stream that ends without `close_notify` is
34
+ * truncation.
35
+ *
36
+ * **Authentication is the caller's.** At `TlsClient`'s `WAIT_AUTH` the
37
+ * records after the server's CertificateVerify stay in the reader and
38
+ * `interest` says `TLS_RECORD_AUTH`: nothing more is read until the caller
39
+ * has checked the chain and the signature and called `authenticate(true)` —
40
+ * or `authenticate(false)` / `reject(alert)`, which fail the connection with
41
+ * the alert sent. Nothing here proves who the server is.
42
+ *
43
+ * **Buffers.** One connection, one object: `output`, `plain` and `scratch`
44
+ * are allocated with it, and `wipe` zeroes the two that hold plaintext. The
45
+ * handshake's messages are sealed straight out of `TlsClient`'s own buffers.
46
+ *
47
+ * **Secrets.** As in `TlsRecordServer`: `TlsClient` holds the handshake's
48
+ * secrets, this object the two application traffic secrets, one readonly
49
+ * field per direction and hash length, with `readHash` and `writeHash` saying
50
+ * which of each pair is live. A secret is copied out of `TlsClient` as it is
51
+ * installed and the client's copy zeroed; the handshake secrets are zeroed
52
+ * once the Finished messages no longer need them; a KeyUpdate overwrites
53
+ * the live field in place. A failure of any kind wipes everything both
54
+ * objects hold once the last alert is sealed; the client's `close_notify`,
55
+ * once sealed, wipes the write direction, and the server's the read
56
+ * direction; `wipe` wipes the rest (TLS-1, TLS-3 and "The client" in
57
+ * `docs/security/tls.md`).
58
+ *
59
+ * Refusals are alerts, never panics. Written from RFC 8446 §4.6.3, §5, §6
60
+ * and §D.4, not ported from another implementation.
61
+ */
62
+ import {
63
+ TLS_LEVEL_APPLICATION,
64
+ TLS_LEVEL_HANDSHAKE,
65
+ TLS_LEVEL_INITIAL,
66
+ TLS_SECRET_CLIENT_APPLICATION,
67
+ TLS_SECRET_CLIENT_HANDSHAKE,
68
+ TLS_SECRET_HANDSHAKE,
69
+ TLS_SECRET_SERVER_APPLICATION,
70
+ TLS_SECRET_SERVER_HANDSHAKE,
71
+ TlsSecretPool,
72
+ tlsHeldClone,
73
+ tlsHeldCopyIn,
74
+ tlsHeldCopyOver,
75
+ tlsZeroHeld,
76
+ } from "nish/net/tls"
77
+ import {
78
+ TLS_CLIENT_STATE_CONNECTED,
79
+ TLS_CLIENT_STATE_FAILED,
80
+ TLS_CLIENT_STATE_START,
81
+ TLS_CLIENT_STATE_WAIT_AUTH,
82
+ TlsClient,
83
+ } from "nish/net/tls/client"
84
+ import {
85
+ TLS_RECORD_AGAIN,
86
+ TLS_RECORD_DATA,
87
+ TLS_RECORD_DONE,
88
+ TLS_RECORD_IDLE_LIMIT,
89
+ TLS_RECORD_INVALID,
90
+ TLS_RECORD_MAX_KEY_UPDATES,
91
+ TLS_RECORD_PIPE,
92
+ TLS_RECORD_RESET,
93
+ TLS_RECORD_STATE_CLOSED,
94
+ TLS_RECORD_STATE_FAILED,
95
+ TLS_RECORD_STATE_HANDSHAKE,
96
+ TLS_RECORD_STATE_OPEN,
97
+ TLS_RECORD_WANT_READ,
98
+ TLS_RECORD_WANT_WRITE,
99
+ } from "nish/net/tls/record-server"
100
+ import { Secret, expose, exposeWith, secret, wipe } from "nish:secret"
101
+ import { SHA256_SIZE } from "nish/crypto/sha256"
102
+ import { SHA384_SIZE } from "nish/crypto/sha512"
103
+ import {
104
+ TLS_ALERT_DECODE_ERROR,
105
+ TLS_ALERT_ILLEGAL_PARAMETER,
106
+ TLS_ALERT_INTERNAL_ERROR,
107
+ TLS_ALERT_UNEXPECTED_MESSAGE,
108
+ TLS_HANDSHAKE_NEW_SESSION_TICKET,
109
+ } from "nish/net/tls/codec"
110
+ import {
111
+ TLS_ALERT_CLOSE_NOTIFY,
112
+ TLS_ALERT_LEVEL_FATAL,
113
+ TLS_ALERT_LEVEL_WARNING,
114
+ TLS_ALERT_USER_CANCELED,
115
+ TLS_CONTENT_ALERT,
116
+ TLS_CONTENT_APPLICATION_DATA,
117
+ TLS_CONTENT_CHANGE_CIPHER_SPEC,
118
+ TLS_CONTENT_HANDSHAKE,
119
+ TLS_HANDSHAKE_KEY_UPDATE,
120
+ TLS_MAX_PLAINTEXT,
121
+ TLS_MAX_RECORD,
122
+ TLS_RECORD_HEADER_SIZE,
123
+ TLS_RECORD_TAG_SIZE,
124
+ TlsRecordProtection,
125
+ TlsRecordReader,
126
+ tlsRecordWindowFits,
127
+ } from "nish/net/tls/record"
128
+
129
+ /**
130
+ * An `interest` bit beside `nish/net/tls/record-server`'s `TLS_RECORD_*`:
131
+ * the server's chain and CertificateVerify wait for the caller's verdict,
132
+ * `authenticate` or `reject`.
133
+ */
134
+ export const TLS_RECORD_AUTH: i32 = 64
135
+
136
+ /** The size of the bytes to send: two whole records, so a record fits beside one partly sent. */
137
+ const TLS_RECORD_CLIENT_OUTPUT_SIZE: i32 = 2 * TLS_MAX_RECORD
138
+ /** The size of the application data the caller has not read: two records' worth. */
139
+ const TLS_RECORD_CLIENT_PLAIN_SIZE: i32 = 2 * TLS_MAX_PLAINTEXT
140
+
141
+ /** A KeyUpdate message: type, a 24-bit length of 1, and `request_update`. */
142
+ const TLS_KEY_UPDATE_SIZE: i32 = 5
143
+ /** A handshake message header: type and a 24-bit length. */
144
+ const TLS_MESSAGE_HEADER_SIZE: i32 = 4
145
+
146
+ /** A typed zero for the offsets below: a bare literal is an `f64` under `--number-mode f64`. */
147
+ const TLS_RECORD_CLIENT_FROM: i32 = 0
148
+
149
+ /**
150
+ * Installs `protection` under `suite` from the traffic secret `held`, from a
151
+ * plain copy that dies with the arena block, zeroed; false, as `install`
152
+ * answers, when the keys do not install. As in `nish/net/tls/record-server`.
153
+ */
154
+ const tlsClientInstallHeld = (protection: TlsRecordProtection, suite: i32, held: Secret<u8[]>): boolean => {
155
+ using _scope = arena()
156
+ const plain: u8[] = expose(held, tlsHeldClone)
157
+ const installed: boolean = protection.install(suite, plain)
158
+ secureZero(plain)
159
+ return installed
160
+ }
161
+
162
+ /**
163
+ * Moves `protection` to the next traffic secret (§7.2) and writes it over
164
+ * `held`'s bytes in place, the KeyUpdate step; false when the keys do not
165
+ * install. As in `nish/net/tls/record-server`.
166
+ */
167
+ const tlsClientAdvanceHeld = (protection: TlsRecordProtection, held: Secret<u8[]>): boolean => {
168
+ using _scope = arena()
169
+ const plain: u8[] = expose(held, tlsHeldClone)
170
+ const installed: boolean = protection.advance(plain)
171
+ exposeWith(held, plain, tlsHeldCopyIn)
172
+ secureZero(plain)
173
+ return installed
174
+ }
175
+
176
+ /**
177
+ * One TLS 1.3 client connection over a stream. Make one per connection
178
+ * around a fresh `TlsClient`, which it starts, and drive it with `receive`,
179
+ * `consume`, `read`, `write` and `authenticate` as `interest` says.
180
+ *
181
+ * The fields are readable: `state`, `alert` (the alert this client sent, or
182
+ * 0), `peerAlert` (the error alert the server sent, or -1), `peerClosed`
183
+ * (the server's `close_notify` arrived), `tls` (the handshake: suite, ALPN,
184
+ * the server's certificates, exporter secret), and the two protections with
185
+ * their sequence numbers. `padding` is how many zero bytes each protected
186
+ * record the client writes carries after its content type (§5.4).
187
+ */
188
+ export class TlsRecordClient {
189
+ tls: TlsClient
190
+ reader: TlsRecordReader
191
+ readProtection: TlsRecordProtection
192
+ writeProtection: TlsRecordProtection
193
+ /** Never installed: seals the compatibility record in the clear once the write side is protected. */
194
+ clearProtection: TlsRecordProtection
195
+ /** Bytes to send are `output[outputStart .. outputEnd)`. */
196
+ output: u8[]
197
+ /** Application data the caller has not read is `plain[plainStart .. plainEnd)`. */
198
+ plain: u8[]
199
+ /** Where a record is opened to. */
200
+ scratch: u8[]
201
+ /** The server's and the client's current application traffic secrets, at both hash lengths. */
202
+ readonly readTraffic256: Secret<u8[]>
203
+ readonly readTraffic384: Secret<u8[]>
204
+ readonly writeTraffic256: Secret<u8[]>
205
+ readonly writeTraffic384: Secret<u8[]>
206
+ readHash: i32 = 0
207
+ writeHash: i32 = 0
208
+ /** The header of a post-handshake message the server has sent part of, and a KeyUpdate's body. */
209
+ postHandshake: u8[]
210
+ state: i32 = 0
211
+ alert: i32 = 0
212
+ peerAlert: i32 = -1
213
+ padding: i32 = 0
214
+ /** The `TLS_LEVEL_*` the server's records are read at. */
215
+ readLevel: i32 = 0
216
+ /** The `TLS_LEVEL_*` the client's records are written at. */
217
+ writeLevel: i32 = 0
218
+ outputStart: i32 = 0
219
+ outputEnd: i32 = 0
220
+ plainStart: i32 = 0
221
+ plainEnd: i32 = 0
222
+ /** How much of `TlsClient`'s Initial-level output is already in records. */
223
+ pendingInitialAt: i32 = 0
224
+ /** How much of `TlsClient`'s Handshake-level output is already in records. */
225
+ pendingHandshakeAt: i32 = 0
226
+ /** How many bytes of the current post-handshake message's header or KeyUpdate are in `postHandshake`. */
227
+ postHandshakeLength: i32 = 0
228
+ /** How many body bytes of a NewSessionTicket are still to come, handed to `TlsClient` as they arrive. */
229
+ ticketLeft: i32 = 0
230
+ idleRecords: i32 = 0
231
+ keyUpdates: i32 = 0
232
+ peerClosed: boolean = false
233
+ ended: boolean = false
234
+ /** The server's first handshake record has arrived, which opens the window its `change_cipher_spec` may come in. */
235
+ started: boolean = false
236
+ /** The client's compatibility `change_cipher_spec` is sent (§D.4). */
237
+ ccsSent: boolean = false
238
+ keyUpdateDue: boolean = false
239
+ closeDue: boolean = false
240
+
241
+ /**
242
+ * Wraps `tls`, which should be fresh, and starts it: the ClientHello is
243
+ * sealed into `output` at once. A `tls` that has already failed, or whose
244
+ * ClientHello cannot be written, fails this connection with its alert
245
+ * queued in the clear.
246
+ */
247
+ constructor(tls: TlsClient) {
248
+ this.tls = tls
249
+ this.reader = new TlsRecordReader()
250
+ this.readProtection = new TlsRecordProtection()
251
+ this.writeProtection = new TlsRecordProtection()
252
+ this.clearProtection = new TlsRecordProtection()
253
+ this.output = new Array<u8>(TLS_RECORD_CLIENT_OUTPUT_SIZE)
254
+ this.plain = new Array<u8>(TLS_RECORD_CLIENT_PLAIN_SIZE)
255
+ this.scratch = new Array<u8>(TLS_MAX_PLAINTEXT)
256
+ this.readTraffic256 = secret(new Array<u8>(SHA256_SIZE))
257
+ this.readTraffic384 = secret(new Array<u8>(SHA384_SIZE))
258
+ this.writeTraffic256 = secret(new Array<u8>(SHA256_SIZE))
259
+ this.writeTraffic384 = secret(new Array<u8>(SHA384_SIZE))
260
+ this.postHandshake = new Array<u8>(TLS_KEY_UPDATE_SIZE)
261
+ this.state = TLS_RECORD_STATE_HANDSHAKE
262
+ let alert: i32 = tls.state === TLS_CLIENT_STATE_FAILED ? tls.alert : 0
263
+ if (alert === 0 && tls.state === TLS_CLIENT_STATE_START) {
264
+ alert = tls.start()
265
+ }
266
+ if (alert !== 0) {
267
+ this.fail(alert)
268
+ return
269
+ }
270
+ this.pump()
271
+ }
272
+
273
+ /**
274
+ * Wipes everything this connection holds: both traffic secrets at both
275
+ * hash lengths, both directions' keys, the plaintext buffers and everything
276
+ * `TlsClient` holds, so `read` has nothing more to give. Call it when the
277
+ * connection is done with.
278
+ */
279
+ wipe(): void {
280
+ wipe(this.readTraffic256)
281
+ wipe(this.readTraffic384)
282
+ wipe(this.writeTraffic256)
283
+ wipe(this.writeTraffic384)
284
+ this.readHash = 0
285
+ this.writeHash = 0
286
+ this.readProtection.clear()
287
+ this.writeProtection.clear()
288
+ secureZero(this.plain)
289
+ this.plainStart = 0
290
+ this.plainEnd = 0
291
+ secureZero(this.scratch)
292
+ secureZero(this.postHandshake)
293
+ this.tls.wipe()
294
+ }
295
+
296
+ /** Wipes the server's application traffic secret and the read keys. */
297
+ wipeRead(): void {
298
+ exposeWith(this.readTraffic256, TLS_RECORD_CLIENT_FROM, tlsZeroHeld)
299
+ exposeWith(this.readTraffic384, TLS_RECORD_CLIENT_FROM, tlsZeroHeld)
300
+ this.readHash = 0
301
+ this.readProtection.clear()
302
+ }
303
+
304
+ /** Wipes the client's application traffic secret and its write keys. */
305
+ wipeWrite(): void {
306
+ exposeWith(this.writeTraffic256, TLS_RECORD_CLIENT_FROM, tlsZeroHeld)
307
+ exposeWith(this.writeTraffic384, TLS_RECORD_CLIENT_FROM, tlsZeroHeld)
308
+ this.writeHash = 0
309
+ this.writeProtection.clear()
310
+ }
311
+
312
+ /**
313
+ * Copies the server's application traffic secret out of `TlsClient` into
314
+ * the field of its hash's length, wipes the client's copy, and installs the
315
+ * read keys from it. False when the secret is not known or the keys do not
316
+ * install.
317
+ */
318
+ keepRead(): boolean {
319
+ const tls: TlsClient = this.tls
320
+ if (!tls.readKnown(TLS_LEVEL_APPLICATION)) {
321
+ return false
322
+ }
323
+ const pool: TlsSecretPool = tls.secrets()
324
+ this.readHash = tls.hashLength
325
+ let installed: boolean = false
326
+ if (this.readHash === SHA384_SIZE) {
327
+ exposeWith(pool.serverApplication, this.readTraffic384, tlsHeldCopyOver)
328
+ tls.wipeSecret(TLS_SECRET_SERVER_APPLICATION)
329
+ installed = tlsClientInstallHeld(this.readProtection, tls.suite, this.readTraffic384)
330
+ } else {
331
+ exposeWith(pool.serverApplication, this.readTraffic256, tlsHeldCopyOver)
332
+ tls.wipeSecret(TLS_SECRET_SERVER_APPLICATION)
333
+ installed = tlsClientInstallHeld(this.readProtection, tls.suite, this.readTraffic256)
334
+ }
335
+ return installed
336
+ }
337
+
338
+ /** `keepRead` for the client's application traffic secret and the write keys. */
339
+ keepWrite(): boolean {
340
+ const tls: TlsClient = this.tls
341
+ if (!tls.writeKnown(TLS_LEVEL_APPLICATION)) {
342
+ return false
343
+ }
344
+ const pool: TlsSecretPool = tls.secrets()
345
+ this.writeHash = tls.hashLength
346
+ let installed: boolean = false
347
+ if (this.writeHash === SHA384_SIZE) {
348
+ exposeWith(pool.clientApplication, this.writeTraffic384, tlsHeldCopyOver)
349
+ tls.wipeSecret(TLS_SECRET_CLIENT_APPLICATION)
350
+ installed = tlsClientInstallHeld(this.writeProtection, tls.suite, this.writeTraffic384)
351
+ } else {
352
+ exposeWith(pool.clientApplication, this.writeTraffic256, tlsHeldCopyOver)
353
+ tls.wipeSecret(TLS_SECRET_CLIENT_APPLICATION)
354
+ installed = tlsClientInstallHeld(this.writeProtection, tls.suite, this.writeTraffic256)
355
+ }
356
+ return installed
357
+ }
358
+
359
+ /** How many bytes `receive` would take now: what the socket should read at most. */
360
+ space(): i32 {
361
+ return this.reader.space()
362
+ }
363
+
364
+ /**
365
+ * Takes bytes of the server's stream, `data[off .. off + len)`, and handles
366
+ * every record they complete. Answers how many it took, fewer than `len`
367
+ * only when its buffers are full, or `TLS_RECORD_INVALID` for a window
368
+ * outside `data`. Once the connection has failed, the server has sent
369
+ * `close_notify`, or the client closed before the handshake was done, bytes
370
+ * are taken and dropped (§6.1).
371
+ */
372
+ receive(data: u8[], off: i32, len: i32): i32 {
373
+ if (!tlsRecordWindowFits(toI32(data.length), off, len)) {
374
+ return TLS_RECORD_INVALID
375
+ }
376
+ if (this.state === TLS_RECORD_STATE_FAILED || this.peerClosed || this.abandoned()) {
377
+ return len
378
+ }
379
+ const taken: i32 = this.reader.push(data, off, len)
380
+ this.process()
381
+ return taken
382
+ }
383
+
384
+ /** The server's TCP stream ended: the orderly end after its `close_notify`, truncation before it. */
385
+ end(): void {
386
+ this.ended = true
387
+ this.process()
388
+ }
389
+
390
+ /**
391
+ * Once the server's stream has ended and every whole record it held is
392
+ * handled, an end without `close_notify` is truncation: the connection
393
+ * fails with nothing more sent, and every key it holds is wiped.
394
+ */
395
+ checkTruncation(): void {
396
+ if (this.ended && !this.peerClosed && this.state !== TLS_RECORD_STATE_FAILED && this.reader.next() <= 0) {
397
+ this.state = TLS_RECORD_STATE_FAILED
398
+ this.dropPending()
399
+ this.wipe()
400
+ }
401
+ }
402
+
403
+ /** The socket failed under the connection: it ends with nothing more sent, and every key it holds wiped. */
404
+ abort(): void {
405
+ this.state = TLS_RECORD_STATE_FAILED
406
+ this.outputStart = 0
407
+ this.outputEnd = 0
408
+ this.dropPending()
409
+ this.wipe()
410
+ }
411
+
412
+ /** Forgets what is owed but not yet sealed: the handshake queues, a KeyUpdate and a close. */
413
+ dropPending(): void {
414
+ this.tls.clearOutput(TLS_LEVEL_INITIAL)
415
+ this.tls.clearOutput(TLS_LEVEL_HANDSHAKE)
416
+ this.pendingInitialAt = 0
417
+ this.pendingHandshakeAt = 0
418
+ this.keyUpdateDue = false
419
+ this.closeDue = false
420
+ }
421
+
422
+ /**
423
+ * Handles every whole record the reader holds, as far as the application
424
+ * data buffer has room for. While `TlsClient` waits for the caller's
425
+ * verdict on the server's certificate the records stay where they are, and
426
+ * `authenticate` comes back for them.
427
+ */
428
+ process(): void {
429
+ while (this.state !== TLS_RECORD_STATE_FAILED && !this.peerClosed && !this.abandoned()) {
430
+ if (this.tls.state === TLS_CLIENT_STATE_WAIT_AUTH) {
431
+ break
432
+ }
433
+ const n: i32 = this.reader.next()
434
+ if (n < 0) {
435
+ this.fail(-n)
436
+ return
437
+ }
438
+ if (n === 0) {
439
+ break
440
+ }
441
+ if (this.readLevel === TLS_LEVEL_APPLICATION && !this.plainHasRoom()) {
442
+ break
443
+ }
444
+ const alert: i32 = this.handleRecord(this.reader.buffer, this.reader.start, n)
445
+ this.reader.consume(n)
446
+ if (alert !== 0) {
447
+ this.fail(alert)
448
+ return
449
+ }
450
+ }
451
+ this.checkTruncation()
452
+ this.pump()
453
+ }
454
+
455
+ /** Whether a whole record's content fits beside what the caller has not read, moving that to the front when it would then fit. */
456
+ plainHasRoom(): boolean {
457
+ const capacity: i32 = TLS_RECORD_CLIENT_PLAIN_SIZE
458
+ if (capacity - (this.plainEnd - this.plainStart) < TLS_MAX_PLAINTEXT) {
459
+ return false
460
+ }
461
+ if (capacity - this.plainEnd < TLS_MAX_PLAINTEXT) {
462
+ const held: i32 = this.plainEnd - this.plainStart
463
+ for (let k: i32 = 0; k < held && this.plainStart + k < toI32(this.plain.length); k++) {
464
+ this.plain[k] = this.plain[this.plainStart + k]
465
+ }
466
+ this.plainStart = 0
467
+ this.plainEnd = held
468
+ }
469
+ return true
470
+ }
471
+
472
+ /** Counts a record that carried nothing for the caller: 0, or `unexpected_message` once there are too many in a row. */
473
+ idle(): i32 {
474
+ this.idleRecords = this.idleRecords + 1
475
+ return this.idleRecords > TLS_RECORD_IDLE_LIMIT ? TLS_ALERT_UNEXPECTED_MESSAGE : 0
476
+ }
477
+
478
+ /**
479
+ * One whole record, `buf[off .. off + n)`: a `change_cipher_spec` is judged
480
+ * on its own; anything else is opened under the read protection into
481
+ * `scratch`, and its content goes to `handleApplicationRecord` once the
482
+ * server's Finished is read, to the handshake before. Answers 0 or the alert.
483
+ */
484
+ handleRecord(buf: u8[], off: i32, n: i32): i32 {
485
+ if (toI32(buf[off]) === TLS_CONTENT_CHANGE_CIPHER_SPEC) {
486
+ return this.handleChangeCipherSpec(buf, off, n - TLS_RECORD_HEADER_SIZE)
487
+ }
488
+ const length: i32 = this.readProtection.open(buf, off, n, this.scratch, TLS_RECORD_CLIENT_FROM)
489
+ if (length < 0) {
490
+ return -length
491
+ }
492
+ if (this.readLevel === TLS_LEVEL_APPLICATION) {
493
+ return this.handleApplicationRecord(length)
494
+ }
495
+ switch (this.readProtection.contentType) {
496
+ case TLS_CONTENT_HANDSHAKE:
497
+ return this.handleHandshake(length)
498
+ case TLS_CONTENT_ALERT:
499
+ // §5.1: an alert may not fall between the records of one handshake message.
500
+ return this.tls.inputLength > 0 ? TLS_ALERT_UNEXPECTED_MESSAGE : this.handleAlert(length)
501
+ default:
502
+ // Application data before the server's Finished, a protected
503
+ // change_cipher_spec (§5), or a type nobody defined.
504
+ return TLS_ALERT_UNEXPECTED_MESSAGE
505
+ }
506
+ }
507
+
508
+ /**
509
+ * The server's compatibility record (§5, §D.4): one byte of 1, in the
510
+ * clear, after its first handshake record and before its Finished, and not
511
+ * between the records of one handshake message. Dropped; anything else is
512
+ * `unexpected_message`.
513
+ */
514
+ handleChangeCipherSpec(buf: u8[], off: i32, length: i32): i32 {
515
+ if (length !== 1 || toI32(buf[off + TLS_RECORD_HEADER_SIZE]) !== 1) {
516
+ return TLS_ALERT_UNEXPECTED_MESSAGE
517
+ }
518
+ if (!this.started || this.readLevel === TLS_LEVEL_APPLICATION || this.tls.inputLength > 0) {
519
+ return TLS_ALERT_UNEXPECTED_MESSAGE
520
+ }
521
+ return this.idle()
522
+ }
523
+
524
+ /** Handshake bytes, `scratch[0 .. length)`, for `TlsClient` at the level they were read at; an empty fragment is `unexpected_message` (§5.1). */
525
+ handleHandshake(length: i32): i32 {
526
+ if (length === 0) {
527
+ return TLS_ALERT_UNEXPECTED_MESSAGE
528
+ }
529
+ this.started = true
530
+ this.idleRecords = 0
531
+ const alert: i32 = this.tls.receive(this.readLevel, this.scratch, TLS_RECORD_CLIENT_FROM, length)
532
+ if (alert !== 0) {
533
+ return alert
534
+ }
535
+ this.advance()
536
+ return 0
537
+ }
538
+
539
+ /**
540
+ * A record under the server's application keys, its `length` bytes of
541
+ * content in `scratch`: application data for the caller, an alert, or
542
+ * post-handshake handshake bytes, which nothing else may interrupt (§5.1).
543
+ */
544
+ handleApplicationRecord(length: i32): i32 {
545
+ const type: i32 = this.readProtection.contentType
546
+ if ((this.postHandshakeLength > 0 || this.ticketLeft > 0) && type !== TLS_CONTENT_HANDSHAKE) {
547
+ return TLS_ALERT_UNEXPECTED_MESSAGE
548
+ }
549
+ switch (type) {
550
+ case TLS_CONTENT_APPLICATION_DATA: {
551
+ if (length === 0) {
552
+ return this.idle()
553
+ }
554
+ for (let k: i32 = 0; k < length && this.plainEnd + k < toI32(this.plain.length); k++) {
555
+ this.plain[this.plainEnd + k] = this.scratch[k]
556
+ }
557
+ this.plainEnd = this.plainEnd + length
558
+ this.idleRecords = 0
559
+ return 0
560
+ }
561
+ case TLS_CONTENT_ALERT:
562
+ return this.handleAlert(length)
563
+ case TLS_CONTENT_HANDSHAKE:
564
+ return length === 0 ? TLS_ALERT_UNEXPECTED_MESSAGE : this.handlePostHandshake(length)
565
+ default:
566
+ return TLS_ALERT_UNEXPECTED_MESSAGE
567
+ }
568
+ }
569
+
570
+ /**
571
+ * An alert, `scratch[0 .. length)` (§6): two bytes, or `decode_error`.
572
+ * `close_notify` once the handshake is done ends the server's stream and
573
+ * wipes the read direction, `user_canceled` is passed over, and any other
574
+ * description — `close_notify` during the handshake among them, so a
575
+ * connection that never authenticated cannot end like one that did — ends
576
+ * the connection with nothing more sent and every key wiped.
577
+ */
578
+ handleAlert(length: i32): i32 {
579
+ if (length !== 2) {
580
+ return TLS_ALERT_DECODE_ERROR
581
+ }
582
+ const description: i32 = toI32(this.scratch[1])
583
+ if (description === TLS_ALERT_CLOSE_NOTIFY && this.state !== TLS_RECORD_STATE_HANDSHAKE) {
584
+ this.peerClosed = true
585
+ this.wipeRead()
586
+ return 0
587
+ }
588
+ if (description === TLS_ALERT_USER_CANCELED) {
589
+ return this.idle()
590
+ }
591
+ this.peerAlert = description
592
+ this.state = TLS_RECORD_STATE_FAILED
593
+ this.outputStart = 0
594
+ this.outputEnd = 0
595
+ this.dropPending()
596
+ this.wipe()
597
+ return 0
598
+ }
599
+
600
+ /**
601
+ * Post-handshake handshake bytes, `scratch[0 .. length)`, which may continue
602
+ * a message an earlier record began. A NewSessionTicket goes to `TlsClient`
603
+ * as it arrives, which reads and drops it. A KeyUpdate is this module's:
604
+ * a length other than 1 is `decode_error`, a `request_update` other than 0
605
+ * or 1 `illegal_parameter`, and one that does not end its record
606
+ * `unexpected_message`, since the keys change after it (§4.6.3, §5.1). Any
607
+ * other type is `unexpected_message`. Each message counts against the idle
608
+ * limit.
609
+ */
610
+ handlePostHandshake(length: i32): i32 {
611
+ let k: i32 = 0
612
+ while (k < length) {
613
+ if (this.ticketLeft > 0) {
614
+ const left: i32 = length - k
615
+ const take: i32 = left < this.ticketLeft ? left : this.ticketLeft
616
+ const alert: i32 = this.tls.receive(TLS_LEVEL_APPLICATION, this.scratch, k, take)
617
+ if (alert !== 0) {
618
+ return alert
619
+ }
620
+ this.ticketLeft = this.ticketLeft - take
621
+ k = k + take
622
+ continue
623
+ }
624
+ if (this.postHandshakeLength === 0) {
625
+ const idle: i32 = this.idle()
626
+ if (idle !== 0) {
627
+ return idle
628
+ }
629
+ }
630
+ if (this.postHandshakeLength < toI32(this.postHandshake.length)) {
631
+ this.postHandshake[this.postHandshakeLength] = this.scratch[k]
632
+ }
633
+ this.postHandshakeLength = this.postHandshakeLength + 1
634
+ const held: i32 = this.postHandshakeLength
635
+ const type: i32 = toI32(this.postHandshake[0])
636
+ if (held === 1 && type !== TLS_HANDSHAKE_KEY_UPDATE && type !== TLS_HANDSHAKE_NEW_SESSION_TICKET) {
637
+ return TLS_ALERT_UNEXPECTED_MESSAGE
638
+ }
639
+ if (held === TLS_MESSAGE_HEADER_SIZE) {
640
+ const body: i32 =
641
+ (toI32(this.postHandshake[1]) << 16) |
642
+ (toI32(this.postHandshake[2]) << 8) |
643
+ toI32(this.postHandshake[3])
644
+ if (type === TLS_HANDSHAKE_NEW_SESSION_TICKET) {
645
+ // The header goes to `TlsClient` with the body, which it reads whole.
646
+ const alert: i32 = this.tls.receive(
647
+ TLS_LEVEL_APPLICATION,
648
+ this.postHandshake,
649
+ TLS_RECORD_CLIENT_FROM,
650
+ TLS_MESSAGE_HEADER_SIZE
651
+ )
652
+ if (alert !== 0) {
653
+ return alert
654
+ }
655
+ this.postHandshakeLength = 0
656
+ this.ticketLeft = body
657
+ } else if (body !== 1) {
658
+ return TLS_ALERT_DECODE_ERROR
659
+ }
660
+ }
661
+ if (held === TLS_KEY_UPDATE_SIZE) {
662
+ const request: i32 = toI32(this.postHandshake[4])
663
+ if (request > 1) {
664
+ return TLS_ALERT_ILLEGAL_PARAMETER
665
+ }
666
+ if (k !== length - 1) {
667
+ return TLS_ALERT_UNEXPECTED_MESSAGE
668
+ }
669
+ this.postHandshakeLength = 0
670
+ if (this.keyUpdates >= TLS_RECORD_MAX_KEY_UPDATES) {
671
+ return TLS_ALERT_UNEXPECTED_MESSAGE
672
+ }
673
+ this.keyUpdates = this.keyUpdates + 1
674
+ return this.updateReadKeys(request === 1)
675
+ }
676
+ k = k + 1
677
+ }
678
+ return 0
679
+ }
680
+
681
+ /**
682
+ * The server's KeyUpdate: its next records are under the next secret
683
+ * (§7.2), the old one wiped; when it asked, the client owes one of its own,
684
+ * which `pump` sends. Answers 0, or `internal_error` when the keys do not
685
+ * install.
686
+ */
687
+ updateReadKeys(requested: boolean): i32 {
688
+ const advanced: boolean =
689
+ this.readHash === SHA384_SIZE
690
+ ? tlsClientAdvanceHeld(this.readProtection, this.readTraffic384)
691
+ : this.readHash !== 0 && tlsClientAdvanceHeld(this.readProtection, this.readTraffic256)
692
+ if (!advanced) {
693
+ return TLS_ALERT_INTERNAL_ERROR
694
+ }
695
+ if (requested) {
696
+ this.keyUpdateDue = true
697
+ }
698
+ return 0
699
+ }
700
+
701
+ /**
702
+ * Picks up what `TlsClient` did with the last bytes or the caller's
703
+ * verdict: the read keys of each level as they appear, and the end of the
704
+ * server's flight, after which its handshake secrets have done their work
705
+ * and are wiped in `TlsClient`.
706
+ */
707
+ advance(): void {
708
+ const tls: TlsClient = this.tls
709
+ if (this.readLevel === TLS_LEVEL_INITIAL && tls.readKnown(TLS_LEVEL_HANDSHAKE)) {
710
+ if (!tlsClientInstallHeld(this.readProtection, tls.suite, tls.secrets().serverHandshake)) {
711
+ this.fail(TLS_ALERT_INTERNAL_ERROR)
712
+ return
713
+ }
714
+ this.readLevel = TLS_LEVEL_HANDSHAKE
715
+ }
716
+ if (tls.state === TLS_CLIENT_STATE_CONNECTED && this.readLevel === TLS_LEVEL_HANDSHAKE) {
717
+ // The server's Finished is verified: the handshake secret and the
718
+ // server's handshake secret are done with.
719
+ tls.wipeSecret(TLS_SECRET_HANDSHAKE)
720
+ tls.wipeSecret(TLS_SECRET_SERVER_HANDSHAKE)
721
+ if (!this.keepRead()) {
722
+ this.fail(TLS_ALERT_INTERNAL_ERROR)
723
+ return
724
+ }
725
+ this.readLevel = TLS_LEVEL_APPLICATION
726
+ if (this.state === TLS_RECORD_STATE_HANDSHAKE) {
727
+ this.state = TLS_RECORD_STATE_OPEN
728
+ }
729
+ }
730
+ this.pump()
731
+ }
732
+
733
+ /** Whether `need` more bytes fit in `output`, moving what is unsent to the front first. */
734
+ outputHasRoom(need: i32): boolean {
735
+ const capacity: i32 = TLS_RECORD_CLIENT_OUTPUT_SIZE
736
+ if (this.outputEnd + need > capacity && this.outputStart > 0) {
737
+ const held: i32 = this.outputEnd - this.outputStart
738
+ for (let k: i32 = 0; k < held && this.outputStart + k < toI32(this.output.length); k++) {
739
+ this.output[k] = this.output[this.outputStart + k]
740
+ }
741
+ this.outputStart = 0
742
+ this.outputEnd = held
743
+ }
744
+ return this.outputEnd + need <= capacity
745
+ }
746
+
747
+ /** The most content one record may carry under the write protection as it stands (§5.4). */
748
+ maxChunk(): i32 {
749
+ return TLS_MAX_PLAINTEXT - this.writePadding()
750
+ }
751
+
752
+ /** The padding the next record carries: `padding` once records are protected, none in the clear. */
753
+ writePadding(): i32 {
754
+ return this.writeProtection.isProtected() ? this.padding : 0
755
+ }
756
+
757
+ /** The room a record of `len` content bytes takes in `output` under the write protection as it stands. */
758
+ recordRoom(len: i32): i32 {
759
+ if (!this.writeProtection.isProtected()) {
760
+ return TLS_RECORD_HEADER_SIZE + len
761
+ }
762
+ return TLS_RECORD_HEADER_SIZE + len + 1 + this.writePadding() + TLS_RECORD_TAG_SIZE
763
+ }
764
+
765
+ /**
766
+ * Seals one record of `type` from `data[off .. off + len)` onto `output`
767
+ * under `protection`. False, writing nothing, when it does not fit; a
768
+ * refusal from `seal` itself fails the connection.
769
+ */
770
+ sealWith(protection: TlsRecordProtection, type: i32, data: u8[], off: i32, len: i32): boolean {
771
+ const sealed: boolean = protection.isProtected()
772
+ const padding: i32 = sealed ? this.padding : 0
773
+ const room: i32 = sealed
774
+ ? TLS_RECORD_HEADER_SIZE + len + 1 + padding + TLS_RECORD_TAG_SIZE
775
+ : TLS_RECORD_HEADER_SIZE + len
776
+ if (!this.outputHasRoom(room)) {
777
+ return false
778
+ }
779
+ const n: i32 = protection.seal(type, data, off, len, padding, this.output, this.outputEnd)
780
+ if (n < 0) {
781
+ this.fail(-n)
782
+ return false
783
+ }
784
+ this.outputEnd = this.outputEnd + n
785
+ return true
786
+ }
787
+
788
+ /** `sealWith` under the write protection. */
789
+ sealRecord(type: i32, data: u8[], off: i32, len: i32): boolean {
790
+ return this.sealWith(this.writeProtection, type, data, off, len)
791
+ }
792
+
793
+ /**
794
+ * The compatibility `change_cipher_spec` (§D.4), in the clear whatever the
795
+ * write side is under, once a connection. False when `output` has no room.
796
+ */
797
+ sealChangeCipherSpec(): boolean {
798
+ if (this.ccsSent) {
799
+ return true
800
+ }
801
+ const ccs: u8[] = [toU8(1)]
802
+ const one: i32 = 1
803
+ // `recordRoom` counts the protected size, which is more than the clear record needs.
804
+ if (
805
+ !this.sealWith(this.clearProtection, TLS_CONTENT_CHANGE_CIPHER_SPEC, ccs, TLS_RECORD_CLIENT_FROM, one)
806
+ ) {
807
+ return false
808
+ }
809
+ this.ccsSent = true
810
+ return true
811
+ }
812
+
813
+ /**
814
+ * Seals as much of `queue[at .. length)` as fits, in records of at most
815
+ * `maxChunk()` bytes, and answers where it stopped. A padding that leaves
816
+ * no room for content fails the connection with `internal_error`.
817
+ */
818
+ sealQueue(queue: u8[], at: i32, length: i32): i32 {
819
+ let next: i32 = at
820
+ const most: i32 = this.maxChunk()
821
+ if (most < 1 && next < length) {
822
+ this.fail(TLS_ALERT_INTERNAL_ERROR)
823
+ return next
824
+ }
825
+ while (next < length && this.state !== TLS_RECORD_STATE_FAILED) {
826
+ const left: i32 = length - next
827
+ const chunk: i32 = left < most ? left : most
828
+ if (!this.sealRecord(TLS_CONTENT_HANDSHAKE, queue, next, chunk)) {
829
+ return next
830
+ }
831
+ next = next + chunk
832
+ }
833
+ return next
834
+ }
835
+
836
+ /**
837
+ * Moves what is owed onto `output` as far as it fits, in the order the
838
+ * server must read it: the ClientHellos in the clear (the compatibility
839
+ * record before a second one), the compatibility record and the handshake
840
+ * write keys once they are known, the client's Finished under them, and
841
+ * then the switch to the application keys, with the client's handshake and
842
+ * application secrets wiped in `TlsClient` once they are installed here. A
843
+ * KeyUpdate owed to the server goes last.
844
+ */
845
+ pump(): void {
846
+ if (this.state === TLS_RECORD_STATE_FAILED) {
847
+ return
848
+ }
849
+ if (this.state === TLS_RECORD_STATE_CLOSED) {
850
+ this.sealClose()
851
+ return
852
+ }
853
+ const tls: TlsClient = this.tls
854
+ if (tls.outputInitialLength > 0) {
855
+ // §D.4: after a HelloRetryRequest the record goes before the second ClientHello.
856
+ if (tls.retried && !this.sealChangeCipherSpec()) {
857
+ return
858
+ }
859
+ this.pendingInitialAt = this.sealQueue(
860
+ tls.outputInitial,
861
+ this.pendingInitialAt,
862
+ tls.outputInitialLength
863
+ )
864
+ if (this.pendingInitialAt < tls.outputInitialLength) {
865
+ return
866
+ }
867
+ tls.clearOutput(TLS_LEVEL_INITIAL)
868
+ this.pendingInitialAt = 0
869
+ }
870
+ // Everything the client sends after its ServerHello is read is under the
871
+ // handshake keys, an alert included, so they go in as soon as they are
872
+ // known; the compatibility record, in the clear, goes first.
873
+ if (this.writeLevel === TLS_LEVEL_INITIAL && tls.writeKnown(TLS_LEVEL_HANDSHAKE)) {
874
+ if (!this.sealChangeCipherSpec()) {
875
+ return
876
+ }
877
+ if (!tlsClientInstallHeld(this.writeProtection, tls.suite, tls.secrets().clientHandshake)) {
878
+ this.fail(TLS_ALERT_INTERNAL_ERROR)
879
+ return
880
+ }
881
+ this.writeLevel = TLS_LEVEL_HANDSHAKE
882
+ }
883
+ if (this.writeLevel === TLS_LEVEL_HANDSHAKE && tls.outputHandshakeLength > 0) {
884
+ this.pendingHandshakeAt = this.sealQueue(
885
+ tls.outputHandshake,
886
+ this.pendingHandshakeAt,
887
+ tls.outputHandshakeLength
888
+ )
889
+ if (this.pendingHandshakeAt < tls.outputHandshakeLength) {
890
+ return
891
+ }
892
+ tls.clearOutput(TLS_LEVEL_HANDSHAKE)
893
+ this.pendingHandshakeAt = 0
894
+ }
895
+ if (this.writeLevel === TLS_LEVEL_HANDSHAKE && tls.state === TLS_CLIENT_STATE_CONNECTED) {
896
+ // The client's Finished is sealed: its handshake secret is done with.
897
+ tls.wipeSecret(TLS_SECRET_CLIENT_HANDSHAKE)
898
+ if (!this.keepWrite()) {
899
+ this.fail(TLS_ALERT_INTERNAL_ERROR)
900
+ return
901
+ }
902
+ this.writeLevel = TLS_LEVEL_APPLICATION
903
+ }
904
+ if (this.keyUpdateDue && this.writeLevel === TLS_LEVEL_APPLICATION && this.sendKeyUpdate(false)) {
905
+ this.keyUpdateDue = false
906
+ }
907
+ }
908
+
909
+ /**
910
+ * Seals a KeyUpdate under the current write keys and moves the write side
911
+ * to the next secret (§4.6.3, §7.2), the old one wiped. False, sending
912
+ * nothing, when it does not fit in `output` yet.
913
+ */
914
+ sendKeyUpdate(request: boolean): boolean {
915
+ const message: u8[] = [toU8(TLS_HANDSHAKE_KEY_UPDATE), toU8(0), toU8(0), toU8(1), toU8(request ? 1 : 0)]
916
+ if (!this.sealRecord(TLS_CONTENT_HANDSHAKE, message, TLS_RECORD_CLIENT_FROM, TLS_KEY_UPDATE_SIZE)) {
917
+ return false
918
+ }
919
+ const advanced: boolean =
920
+ this.writeHash === SHA384_SIZE
921
+ ? tlsClientAdvanceHeld(this.writeProtection, this.writeTraffic384)
922
+ : this.writeHash !== 0 && tlsClientAdvanceHeld(this.writeProtection, this.writeTraffic256)
923
+ if (!advanced) {
924
+ this.fail(TLS_ALERT_INTERNAL_ERROR)
925
+ return false
926
+ }
927
+ return true
928
+ }
929
+
930
+ /**
931
+ * Sends a KeyUpdate now, asking the server to update too when
932
+ * `requestPeer` (§4.6.3). Answers 0, `TLS_RECORD_AGAIN` while the handshake
933
+ * is under way or `output` is full, `TLS_RECORD_PIPE` once the client has
934
+ * closed or the connection failed, or `TLS_RECORD_RESET` when the next keys
935
+ * do not install.
936
+ */
937
+ keyUpdate(requestPeer: boolean): i32 {
938
+ if (this.state === TLS_RECORD_STATE_FAILED || this.state === TLS_RECORD_STATE_CLOSED) {
939
+ return TLS_RECORD_PIPE
940
+ }
941
+ this.pump()
942
+ if (this.state !== TLS_RECORD_STATE_OPEN || this.writeLevel !== TLS_LEVEL_APPLICATION) {
943
+ return this.state === TLS_RECORD_STATE_FAILED ? TLS_RECORD_RESET : TLS_RECORD_AGAIN
944
+ }
945
+ if (this.keyUpdateDue || !this.sendKeyUpdate(requestPeer)) {
946
+ return this.state === TLS_RECORD_STATE_FAILED ? TLS_RECORD_RESET : TLS_RECORD_AGAIN
947
+ }
948
+ return 0
949
+ }
950
+
951
+ /**
952
+ * Fails the connection with `alert`, sent as a fatal alert under whatever
953
+ * the client writes with now. What was still owed is dropped, and once the
954
+ * alert is sealed every key the connection holds is wiped.
955
+ */
956
+ fail(alert: i32): void {
957
+ if (this.state === TLS_RECORD_STATE_FAILED) {
958
+ return
959
+ }
960
+ const closed: boolean = this.state === TLS_RECORD_STATE_CLOSED
961
+ this.dropPending()
962
+ this.state = TLS_RECORD_STATE_FAILED
963
+ this.alert = alert
964
+ // A client that has sent its close_notify sends nothing after it (§6.1).
965
+ if (!closed) {
966
+ this.sendAlert(TLS_ALERT_LEVEL_FATAL, alert)
967
+ }
968
+ this.wipe()
969
+ }
970
+
971
+ /** Seals an alert record of `level` and `description`; false, sending nothing, when `output` has no room for it. */
972
+ sendAlert(level: i32, description: i32): boolean {
973
+ const body: u8[] = [toU8(level), toU8(description)]
974
+ const two: i32 = 2
975
+ return this.sealRecord(TLS_CONTENT_ALERT, body, TLS_RECORD_CLIENT_FROM, two)
976
+ }
977
+
978
+ /** Lets the first `n` bytes of `output` go, once the socket has taken them, and seals what was waiting for the room. */
979
+ consume(n: i32): void {
980
+ const held: i32 = this.outputEnd - this.outputStart
981
+ let take: i32 = n < held ? n : held
982
+ if (take < 0) {
983
+ take = 0
984
+ }
985
+ this.outputStart = this.outputStart + take
986
+ if (this.outputStart === this.outputEnd) {
987
+ this.outputStart = 0
988
+ this.outputEnd = 0
989
+ }
990
+ this.pump()
991
+ }
992
+
993
+ /**
994
+ * Application data for the server, `data[off .. off + len)`: seals as much
995
+ * as fits, in records of at most 2^14 bytes, and answers how much that
996
+ * was. A KeyUpdate goes first when the key has protected `recordLimit`
997
+ * records, or the server asked for one. `TLS_RECORD_AGAIN` while the
998
+ * handshake is under way or nothing fits, `TLS_RECORD_PIPE` once the client
999
+ * has closed or the connection failed, `TLS_RECORD_INVALID` for a window
1000
+ * outside `data`.
1001
+ */
1002
+ write(data: u8[], off: i32, len: i32): i32 {
1003
+ if (!tlsRecordWindowFits(toI32(data.length), off, len)) {
1004
+ return TLS_RECORD_INVALID
1005
+ }
1006
+ if (this.state === TLS_RECORD_STATE_FAILED || this.state === TLS_RECORD_STATE_CLOSED) {
1007
+ return TLS_RECORD_PIPE
1008
+ }
1009
+ this.pump()
1010
+ if (
1011
+ this.state !== TLS_RECORD_STATE_OPEN ||
1012
+ this.keyUpdateDue ||
1013
+ this.writeLevel !== TLS_LEVEL_APPLICATION
1014
+ ) {
1015
+ return this.state === TLS_RECORD_STATE_FAILED ? TLS_RECORD_RESET : TLS_RECORD_AGAIN
1016
+ }
1017
+ let done: i32 = 0
1018
+ while (done < len && this.state === TLS_RECORD_STATE_OPEN) {
1019
+ if (this.writeProtection.sequence >= this.writeProtection.recordLimit && !this.sendKeyUpdate(false)) {
1020
+ break
1021
+ }
1022
+ const most: i32 = this.maxChunk()
1023
+ if (most < 1) {
1024
+ this.fail(TLS_ALERT_INTERNAL_ERROR)
1025
+ break
1026
+ }
1027
+ const left: i32 = len - done
1028
+ let chunk: i32 = left < most ? left : most
1029
+ this.outputHasRoom(this.recordRoom(chunk))
1030
+ const room: i32 =
1031
+ TLS_RECORD_CLIENT_OUTPUT_SIZE - this.outputEnd - this.recordRoom(TLS_RECORD_CLIENT_FROM)
1032
+ if (room < chunk) {
1033
+ chunk = room
1034
+ }
1035
+ if (chunk <= 0 || !this.sealRecord(TLS_CONTENT_APPLICATION_DATA, data, off + done, chunk)) {
1036
+ break
1037
+ }
1038
+ done = done + chunk
1039
+ }
1040
+ if (done === 0 && len > 0) {
1041
+ return this.state === TLS_RECORD_STATE_FAILED ? TLS_RECORD_RESET : TLS_RECORD_AGAIN
1042
+ }
1043
+ return done
1044
+ }
1045
+
1046
+ /**
1047
+ * Application data from the server, into `buf[off .. off + len)`: the
1048
+ * count; 0 at the end of its stream (its `close_notify`) or for a `len` of
1049
+ * 0; `TLS_RECORD_AGAIN` when nothing is waiting; `TLS_RECORD_RESET` once
1050
+ * the connection has failed; `TLS_RECORD_INVALID` for a window outside
1051
+ * `buf`.
1052
+ */
1053
+ read(buf: u8[], off: i32, len: i32): i32 {
1054
+ if (!tlsRecordWindowFits(toI32(buf.length), off, len)) {
1055
+ return TLS_RECORD_INVALID
1056
+ }
1057
+ if (len === 0) {
1058
+ return 0
1059
+ }
1060
+ const avail: i32 = this.plainEnd - this.plainStart
1061
+ if (avail > 0) {
1062
+ const n: i32 = avail < len ? avail : len
1063
+ for (let k: i32 = 0; k < n && off + k < toI32(buf.length); k++) {
1064
+ buf[off + k] = this.plain[this.plainStart + k]
1065
+ }
1066
+ this.plainStart = this.plainStart + n
1067
+ if (this.plainStart === this.plainEnd) {
1068
+ this.plainStart = 0
1069
+ this.plainEnd = 0
1070
+ }
1071
+ this.process()
1072
+ return n
1073
+ }
1074
+ if (this.state === TLS_RECORD_STATE_FAILED) {
1075
+ return TLS_RECORD_RESET
1076
+ }
1077
+ return this.peerClosed ? 0 : TLS_RECORD_AGAIN
1078
+ }
1079
+
1080
+ /**
1081
+ * Closes the client's side (§6.1): a `close_notify`, after which `write`
1082
+ * answers `TLS_RECORD_PIPE`. Application data already written stays ahead
1083
+ * of the alert; the write keys are wiped once it is sealed. Closed before
1084
+ * the handshake is done — before the client's Finished is sealed — the
1085
+ * client reads nothing more, and the read keys and everything `TlsClient`
1086
+ * holds are wiped at once. Nothing happens once
1087
+ * the connection has failed or closed.
1088
+ */
1089
+ close(): void {
1090
+ if (this.state === TLS_RECORD_STATE_FAILED || this.state === TLS_RECORD_STATE_CLOSED) {
1091
+ return
1092
+ }
1093
+ this.dropPending()
1094
+ // Until the client's Finished is sealed the handshake is not done, even
1095
+ // once the server's Finished has opened the connection, and now it never
1096
+ // will be.
1097
+ if (this.writeLevel !== TLS_LEVEL_APPLICATION) {
1098
+ this.wipeRead()
1099
+ this.tls.wipe()
1100
+ }
1101
+ this.state = TLS_RECORD_STATE_CLOSED
1102
+ this.closeDue = true
1103
+ this.sealClose()
1104
+ }
1105
+
1106
+ /** Whether the client closed before its Finished was sealed, so the server's records are not read. */
1107
+ abandoned(): boolean {
1108
+ return this.state === TLS_RECORD_STATE_CLOSED && this.writeLevel !== TLS_LEVEL_APPLICATION
1109
+ }
1110
+
1111
+ /** Seals the `close_notify` `close` owes once `output` has room, then wipes the write direction. */
1112
+ sealClose(): void {
1113
+ if (this.closeDue && this.sendAlert(TLS_ALERT_LEVEL_WARNING, TLS_ALERT_CLOSE_NOTIFY)) {
1114
+ this.closeDue = false
1115
+ this.wipeWrite()
1116
+ }
1117
+ }
1118
+
1119
+ /**
1120
+ * The caller's verdict on the server's certificate chain and
1121
+ * CertificateVerify, while `interest` says `TLS_RECORD_AUTH`
1122
+ * (`TlsClient.authenticate`): `true` reads on to the server's Finished and
1123
+ * seals the client's; `false` fails the connection with `bad_certificate`
1124
+ * sent. Answers 0, or a non-zero alert once the connection has failed
1125
+ * (`failure`) — a verdict given before one is due answers
1126
+ * `internal_error`.
1127
+ */
1128
+ authenticate(ok: boolean): i32 {
1129
+ if (this.state !== TLS_RECORD_STATE_HANDSHAKE) {
1130
+ return this.state === TLS_RECORD_STATE_FAILED ? this.failure() : TLS_ALERT_INTERNAL_ERROR
1131
+ }
1132
+ const alert: i32 = this.tls.authenticate(ok)
1133
+ if (alert !== 0) {
1134
+ this.fail(alert)
1135
+ return alert
1136
+ }
1137
+ this.advance()
1138
+ this.process()
1139
+ return this.state === TLS_RECORD_STATE_FAILED ? this.failure() : 0
1140
+ }
1141
+
1142
+ /** Refuses the server with the caller's `alert` (`TlsClient.reject`), sent; answers the alert. */
1143
+ reject(alert: i32): i32 {
1144
+ if (this.state !== TLS_RECORD_STATE_HANDSHAKE) {
1145
+ return this.state === TLS_RECORD_STATE_FAILED ? this.failure() : TLS_ALERT_INTERNAL_ERROR
1146
+ }
1147
+ const sent: i32 = this.tls.reject(alert)
1148
+ this.fail(sent)
1149
+ return sent
1150
+ }
1151
+
1152
+ /**
1153
+ * Why a failed connection failed, never 0: the alert the client sent, else
1154
+ * the server's error alert, else `internal_error` for a failure with no
1155
+ * alert of its own (truncation, `abort`, `close_notify` mid-handshake).
1156
+ */
1157
+ failure(): i32 {
1158
+ if (this.alert !== 0) {
1159
+ return this.alert
1160
+ }
1161
+ return this.peerAlert > 0 ? this.peerAlert : TLS_ALERT_INTERNAL_ERROR
1162
+ }
1163
+
1164
+ /**
1165
+ * What the connection wants next, as `TLS_RECORD_*` bits: read the socket,
1166
+ * write it, `read`, the caller's verdict on the server (`TLS_RECORD_AUTH`),
1167
+ * or nothing more ever (`TLS_RECORD_DONE`).
1168
+ */
1169
+ interest(): i32 {
1170
+ let bits: i32 = 0
1171
+ const sending: boolean = this.outputEnd > this.outputStart || this.closeDue
1172
+ if (sending) {
1173
+ bits = bits | TLS_RECORD_WANT_WRITE
1174
+ }
1175
+ const failed: boolean = this.state === TLS_RECORD_STATE_FAILED
1176
+ if ((failed || this.state === TLS_RECORD_STATE_CLOSED) && !sending) {
1177
+ bits = bits | TLS_RECORD_DONE
1178
+ }
1179
+ const live: boolean = this.state === TLS_RECORD_STATE_HANDSHAKE || this.state === TLS_RECORD_STATE_OPEN
1180
+ const blocked: boolean = this.reader.next() > 0
1181
+ if (live && !this.peerClosed && !this.ended && !blocked && this.reader.space() > 0) {
1182
+ bits = bits | TLS_RECORD_WANT_READ
1183
+ }
1184
+ if (this.plainEnd > this.plainStart || (this.peerClosed && live)) {
1185
+ bits = bits | TLS_RECORD_DATA
1186
+ }
1187
+ if (this.state === TLS_RECORD_STATE_HANDSHAKE && this.tls.state === TLS_CLIENT_STATE_WAIT_AUTH) {
1188
+ bits = bits | TLS_RECORD_AUTH
1189
+ }
1190
+ return bits
1191
+ }
1192
+ }