@amritk/nish-x86_64-linux 0.17.0 → 0.20.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 (61) hide show
  1. package/INSTALL.md +43 -25
  2. package/bin/nish +0 -0
  3. package/package.json +2 -2
  4. package/runtime/nish.d.ts +55 -5
  5. package/runtime/nish.h +231 -20
  6. package/runtime/nish.mjs +16 -0
  7. package/runtime/runtime-aes.c +490 -0
  8. package/runtime/runtime-cross.c +366 -0
  9. package/runtime/runtime-host.c +7 -6
  10. package/runtime/runtime-net.c +93 -34
  11. package/runtime/runtime-os.c +213 -15
  12. package/runtime/runtime-parallel.c +590 -107
  13. package/runtime/runtime-simd.c +203 -0
  14. package/runtime/runtime-wasm.c +16 -3
  15. package/runtime/runtime.c +89 -23
  16. package/runtime/shim.mjs +71 -1
  17. package/scripts/build.sh +225 -20
  18. package/std/README.md +86 -55
  19. package/std/bytes.ts +163 -0
  20. package/std/crypto/LICENSE-libsecp256k1 +19 -0
  21. package/std/crypto/aes.ts +238 -76
  22. package/std/crypto/chacha20poly1305.ts +43 -9
  23. package/std/crypto/ed25519.ts +840 -0
  24. package/std/crypto/hkdf.ts +304 -12
  25. package/std/crypto/hmac.ts +245 -53
  26. package/std/crypto/p256.ts +1376 -42
  27. package/std/crypto/x25519.ts +55 -37
  28. package/std/crypto/x509-random.ts +71 -0
  29. package/std/crypto/x509.ts +9 -9
  30. package/std/json-tokenizer.ts +1296 -0
  31. package/std/json-typed.ts +1014 -0
  32. package/std/json-value.ts +1163 -0
  33. package/std/json.ts +1412 -70
  34. package/std/net/buffer-pool.ts +151 -0
  35. package/std/net/hpack.ts +416 -112
  36. package/std/net/http-fields.ts +93 -0
  37. package/std/net/http1-server.ts +417 -55
  38. package/std/net/http1.ts +31 -6
  39. package/std/net/http2-tls.ts +112 -24
  40. package/std/net/http2.ts +333 -75
  41. package/std/net/http3-server.ts +231 -45
  42. package/std/net/http3.ts +187 -28
  43. package/std/net/prefork.ts +412 -0
  44. package/std/net/quic-conn-params.ts +86 -16
  45. package/std/net/quic-listener.ts +576 -219
  46. package/std/net/quic-packet.ts +443 -58
  47. package/std/net/quic-recovery.ts +361 -30
  48. package/std/net/quic-stream.ts +434 -28
  49. package/std/net/quic.ts +922 -206
  50. package/std/net/router.ts +676 -0
  51. package/std/net/tls/codec.ts +426 -27
  52. package/std/net/tls/record-server.ts +478 -132
  53. package/std/net/tls/record.ts +133 -42
  54. package/std/net/tls/schedule.ts +464 -55
  55. package/std/net/tls/ticket.ts +610 -0
  56. package/std/net/tls-tcp.ts +204 -57
  57. package/std/net/tls.ts +1132 -305
  58. package/std/net/url.ts +571 -0
  59. package/std/random.ts +94 -0
  60. package/std/text.ts +77 -0
  61. package/std/threads.ts +438 -14
@@ -32,26 +32,42 @@
32
32
  * are refused. A stream that ends without `close_notify` is truncation, but
33
33
  * only once the records still waiting for the caller have been read.
34
34
  *
35
- * **Fixed buffers.** The record being read, the bytes to send and the
36
- * application data waiting for the caller each live in an array allocated
37
- * once, in the constructor, so a connection that has finished its handshake
38
- * allocates nothing that outlives a record, and a slot can be handed the
39
- * next connection with `start`. The handshake's messages are sealed straight
40
- * out of `TlsServer`'s own buffers, and the application secrets are copied
41
- * into arrays of this slot's, so a slot that hands its `TlsServer` the next
42
- * connection with `restart` runs every handshake after the first without
43
- * moving the arena, whichever suite it negotiates; a KeyUpdate's key
44
- * install leaves nothing either (TLS-3 in `docs/security/tls.md`). When the caller leaves application data unread
35
+ * **Borrowed buffers.** The record being read, the record being opened, the
36
+ * bytes to send and the application data waiting for the caller each live in
37
+ * an array borrowed from a `TlsRecordPools` when the connection first needs
38
+ * it, kept while the connection lasts, and given back zeroed with
39
+ * `BufferPool.giveSecret` when it ends (`start`, `wipe`, `release`), so a
40
+ * slot with no connection holds none of them, a connection that has finished
41
+ * its handshake allocates nothing that outlives a record, and a slot can be
42
+ * handed the next connection with `start`. Each give-back zeroes the whole
43
+ * buffer, so it happens once a connection rather than once a record. The handshake's messages are
44
+ * sealed straight out of `TlsServer`'s own buffers, and the application
45
+ * secrets are copied into this slot's fields, so a slot that hands its
46
+ * `TlsServer` the next connection with `restart` runs every handshake after
47
+ * the first without moving the arena, whichever suite it negotiates; a
48
+ * KeyUpdate's key install leaves nothing either (TLS-3 in
49
+ * `docs/security/tls.md`). When the caller leaves application data unread
45
50
  * for long enough to fill its buffer, records stay in the reader and
46
51
  * `interest` stops asking for reads, which is how the peer is made to wait.
47
52
  *
48
- * **Secrets.** Each traffic secret is copied out of `TlsServer` as it is
49
- * installed and the server's copy is wiped with `secureZero`, as are the
50
- * handshake secret once the flight is signed and the caller's ephemeral key
51
- * once the ServerHello is written, or the handshake fails. The two application traffic secrets live
52
- * on here, for KeyUpdate: each is overwritten in place by the next, and both
53
- * are wiped, with both directions' keys, by `wipeKeys` when the carrier
54
- * closes the slot and by `start`. What stays unwiped is TLS-1's and TLS-2's.
53
+ * **Secrets.** Each traffic secret is a `Secret<u8[]>` (`nish:secret`):
54
+ * `TlsServer` holds the handshake's in its `TlsSecretPool`s, and this slot
55
+ * holds the two application traffic secrets in four readonly fields, one per
56
+ * direction and hash length, with `readHash` and `writeHash` saying which of
57
+ * each pair is live (0 when neither is). A secret is copied out of
58
+ * `TlsServer` as it is installed and the server's copy is zeroed, as are the
59
+ * handshake secret once the flight is signed and the ephemeral key once the
60
+ * ServerHello is written, or the handshake fails; a KeyUpdate overwrites the
61
+ * live field in place with the next secret. Every way the connection ends
62
+ * wipes what it holds: a failure of any kind (`fail`, the client's error
63
+ * alert, a truncated stream, `abort`) wipes both directions' keys, both
64
+ * secrets and everything `TlsServer` still holds once the last alert is
65
+ * sealed; the server's `close_notify`, once sealed, wipes the write
66
+ * direction, the client's the read direction, and `close` the resumption
67
+ * master secret; and `wipe` (which `wipeKeys` calls when the carrier closes
68
+ * the slot) and `start` wipe the rest. No secret leaves either object but
69
+ * through `expose`, so a secret cannot be logged or kept outside
70
+ * this module and `nish/net/tls` (TLS-1 in `docs/security/tls.md`).
55
71
  *
56
72
  * Refusals are alerts, never panics. Written from RFC 8446 §4.6.3, §5, §6
57
73
  * and §D.4, not ported from another implementation.
@@ -65,8 +81,20 @@ import {
65
81
  TLS_STATE_WAIT_CLIENT_HELLO,
66
82
  TLS_STATE_WAIT_FINISHED,
67
83
  TLS_STATE_WAIT_SIGNATURE,
84
+ TLS_STATE_WAIT_TICKET,
85
+ TLS_SECRET_CLIENT_APPLICATION,
86
+ TLS_SECRET_CLIENT_HANDSHAKE,
87
+ TLS_SECRET_HANDSHAKE,
88
+ TLS_SECRET_SERVER_APPLICATION,
89
+ TLS_SECRET_SERVER_HANDSHAKE,
68
90
  TlsServer,
91
+ TlsSecretPool,
92
+ tlsHeldClone,
93
+ tlsHeldCopyIn,
94
+ tlsHeldCopyOver,
69
95
  } from "nish/net/tls"
96
+ import { TlsTicketKeyInfo, TlsTicketStrikes } from "nish/net/tls/ticket"
97
+ import { Secret, expose, exposeWith, secret, wipe } from "nish:secret"
70
98
  import { SHA256_SIZE } from "nish/crypto/sha256"
71
99
  import { SHA384_SIZE } from "nish/crypto/sha512"
72
100
  import {
@@ -93,6 +121,7 @@ import {
93
121
  TlsRecordReader,
94
122
  tlsRecordWindowFits,
95
123
  } from "nish/net/tls/record"
124
+ import { BufferPool } from "nish/net/buffer-pool"
96
125
 
97
126
  // ---- States ----------------------------------------------------------------------
98
127
 
@@ -117,6 +146,12 @@ export const TLS_RECORD_DATA: i32 = 4
117
146
  export const TLS_RECORD_SIGN: i32 = 8
118
147
  /** Nothing more will happen: the connection failed or closed, and everything to send is sent. */
119
148
  export const TLS_RECORD_DONE: i32 = 16
149
+ /**
150
+ * A ticket is the carrier's to handle, with the keys and strike register it
151
+ * lends: during the handshake the ClientHello offers one, so `resume` reads
152
+ * it; once the connection is open the client may be issued one, so `issue`.
153
+ */
154
+ export const TLS_RECORD_TICKET: i32 = 32
120
155
 
121
156
  /** What `read` and `write` answer once the connection has failed: Linux's ECONNRESET, as `nish:net` spells it. */
122
157
  export const TLS_RECORD_RESET: i32 = -104
@@ -133,6 +168,41 @@ const TLS_RECORD_OUTPUT_SIZE: i32 = 2 * TLS_MAX_RECORD
133
168
  const TLS_RECORD_PLAIN_SIZE: i32 = 2 * TLS_MAX_PLAINTEXT
134
169
  /** Where a record's content is opened to before it is handed on: the most content a record carries. */
135
170
  const TLS_RECORD_SCRATCH_SIZE: i32 = TLS_MAX_PLAINTEXT
171
+ /**
172
+ * The four pools a connection's record buffers are borrowed from: `output`,
173
+ * `plain` and `scratch` for `TlsRecordServer`'s fields of those names, and
174
+ * `reader` for its `TlsRecordReader`. A carrier with many slots makes one set
175
+ * with a ceiling of its slot count and points every slot at it with
176
+ * `TlsRecordServer.borrowFrom`, so a full server holds what preallocating
177
+ * every slot would have held and an idle one holds only what earlier
178
+ * connections left on the free lists. Each buffer goes back zeroed
179
+ * (`BufferPool.giveSecret`): it held plaintext, or handshake bytes.
180
+ */
181
+ export class TlsRecordPools {
182
+ output: BufferPool
183
+ plain: BufferPool
184
+ scratch: BufferPool
185
+ reader: BufferPool
186
+
187
+ /** Four empty pools of `ceiling` buffers each; nothing is made until a slot needs it. */
188
+ constructor(ceiling: i32) {
189
+ this.output = new BufferPool("TlsRecordServer.output", TLS_RECORD_OUTPUT_SIZE, ceiling)
190
+ this.plain = new BufferPool("TlsRecordServer.plain", TLS_RECORD_PLAIN_SIZE, ceiling)
191
+ this.scratch = new BufferPool("TlsRecordServer.scratch", TLS_RECORD_SCRATCH_SIZE, ceiling)
192
+ this.reader = new BufferPool("TlsRecordReader.buffer", TLS_MAX_RECORD, ceiling)
193
+ }
194
+
195
+ /** How many buffers of the four pools are lent now: 0 once every connection has given its back. */
196
+ lent(): i32 {
197
+ return this.output.lent() + this.plain.lent() + this.scratch.lent() + this.reader.lent()
198
+ }
199
+
200
+ /** How many buffers the four pools have made: their high-water mark together. */
201
+ made(): i32 {
202
+ return this.output.made + this.plain.made + this.scratch.made + this.reader.made
203
+ }
204
+ }
205
+
136
206
  /** A KeyUpdate message: type, a 24-bit length of 1, and `request_update`. */
137
207
  const TLS_KEY_UPDATE_SIZE: i32 = 5
138
208
  /** Where ServerHello and HelloRetryRequest hold `legacy_session_id_echo`'s length: past the header, version and random. */
@@ -164,22 +234,42 @@ export const TLS_RECORD_MAX_KEY_UPDATES: i32 = 64
164
234
  /** A typed zero for the offsets below: a bare literal is an `f64` under `--number-mode f64`. */
165
235
  const TLS_RECORD_SERVER_FROM: i32 = 0
166
236
 
237
+ /** Zeroes the bytes a traffic secret field holds, for `exposeWith`; answers 0. */
238
+ const tlsTrafficZero = (held: u8[], answer: i32): i32 => {
239
+ wipe(held)
240
+ return answer
241
+ }
242
+
167
243
  /**
168
- * Copies a traffic secret out of `TlsServer` into `long` when it is that
169
- * array's length and into `short` otherwise, so the server's own can be wiped
170
- * while this one lives on, and answers the array; `none` when there is no
171
- * secret.
244
+ * Installs `protection` under `suite` from the traffic secret `held`. The
245
+ * keys are derived from a plain copy that dies with the arena block and is
246
+ * zeroed before it ends; false, as `install` answers, when they do not
247
+ * install. Not `TlsRecordProtection.installHeld`: the `*Held` derivations
248
+ * under it cannot open an arena block (`nish/crypto/hkdf`), so each install
249
+ * would leave its HKDF temporaries in the arena, and a slot that restarts a
250
+ * thousand times must leave `Arena.mark()` where it was.
172
251
  */
173
- const tlsKeepSecret = (secret: u8[] | null, short: u8[], long: u8[], none: u8[]): u8[] => {
174
- if (secret === null) {
175
- return none
176
- }
177
- const length: i32 = toI32(secret.length)
178
- const out: u8[] = length === toI32(long.length) ? long : short
179
- for (let k: i32 = 0; k < toI32(out.length) && k < length; k++) {
180
- out[k] = secret[k]
181
- }
182
- return out
252
+ const tlsInstallHeld = (protection: TlsRecordProtection, suite: i32, held: Secret<u8[]>): boolean => {
253
+ using _scope = arena()
254
+ const plain: u8[] = expose(held, tlsHeldClone)
255
+ const installed: boolean = protection.install(suite, plain)
256
+ secureZero(plain)
257
+ return installed
258
+ }
259
+
260
+ /**
261
+ * Moves `protection` to the next traffic secret (§7.2) and writes it over
262
+ * `held`'s bytes in place, the KeyUpdate step: the plain copy it is derived
263
+ * on dies with the arena block, zeroed. False, as `advance` answers, when
264
+ * the keys do not install.
265
+ */
266
+ const tlsAdvanceHeld = (protection: TlsRecordProtection, held: Secret<u8[]>): boolean => {
267
+ using _scope = arena()
268
+ const plain: u8[] = expose(held, tlsHeldClone)
269
+ const installed: boolean = protection.advance(plain)
270
+ exposeWith(held, plain, tlsHeldCopyIn)
271
+ secureZero(plain)
272
+ return installed
183
273
  }
184
274
 
185
275
  /**
@@ -200,20 +290,27 @@ export class TlsRecordServer {
200
290
  reader: TlsRecordReader
201
291
  readProtection: TlsRecordProtection
202
292
  writeProtection: TlsRecordProtection
203
- /** Bytes to send are `output[outputStart .. outputEnd)`. */
293
+ /** Bytes to send are `output[outputStart .. outputEnd)`; `empty` until a record is sealed. */
204
294
  output: u8[]
205
- /** Application data the caller has not read is `plain[plainStart .. plainEnd)`. */
295
+ /** Application data the caller has not read is `plain[plainStart .. plainEnd)`; `empty` until a record brings some. */
206
296
  plain: u8[]
297
+ /** Where a record is opened to; `empty` until the first protected record. */
207
298
  scratch: u8[]
208
- /** The client's and the server's current application traffic secrets, for KeyUpdate. */
209
- readSecret: u8[]
210
- writeSecret: u8[]
211
- /** The arrays those two point into, one of each hash's length, and the empty one they point at before. */
212
- readSecret256: u8[]
213
- readSecret384: u8[]
214
- writeSecret256: u8[]
215
- writeSecret384: u8[]
216
- none: u8[]
299
+ /** The zero-length array a buffer field is while it holds no borrowed buffer. */
300
+ empty: u8[]
301
+ /** Where `output`, `plain`, `scratch` and the reader's buffer are borrowed from. */
302
+ pools: TlsRecordPools
303
+ /**
304
+ * The client's and the server's current application traffic secrets, for
305
+ * KeyUpdate, each held at both hash lengths: `readHash` and `writeHash`
306
+ * say which of a pair holds the secret, and are 0 before it is known.
307
+ */
308
+ readonly readTraffic256: Secret<u8[]>
309
+ readonly readTraffic384: Secret<u8[]>
310
+ readonly writeTraffic256: Secret<u8[]>
311
+ readonly writeTraffic384: Secret<u8[]>
312
+ readHash: i32 = 0
313
+ writeHash: i32 = 0
217
314
  /** A post-handshake message the client has sent part of. */
218
315
  postHandshake: u8[]
219
316
  state: i32 = 0
@@ -226,19 +323,25 @@ export class TlsRecordServer {
226
323
  writeLevel: i32 = 0
227
324
  outputStart: i32 = 0
228
325
  outputEnd: i32 = 0
326
+ /** How far into `output` this borrowing has written, which says whether its give-back zeroes. */
327
+ outputUsed: i32 = 0
229
328
  plainStart: i32 = 0
230
329
  plainEnd: i32 = 0
330
+ /** How far into `plain` this borrowing has written. */
331
+ plainUsed: i32 = 0
231
332
  /** How much of `TlsServer`'s Initial-level output is already in records. */
232
333
  pendingInitialAt: i32 = 0
233
334
  /** How much of `TlsServer`'s Handshake-level output is already in records. */
234
335
  pendingHandshakeAt: i32 = 0
235
- /** The server's flight is signed and finished, so its Handshake-level output may be sealed. */
236
- flightReady: boolean = false
336
+ /** How much of `TlsServer`'s Application-level output, a NewSessionTicket, is already in records. */
337
+ pendingApplicationAt: i32 = 0
237
338
  postHandshakeLength: i32 = 0
238
339
  /** Records in a row that carried nothing for the caller, against `TLS_RECORD_IDLE_LIMIT`. */
239
340
  idleRecords: i32 = 0
240
341
  /** KeyUpdates taken from the client, against `TLS_RECORD_MAX_KEY_UPDATES`. */
241
342
  keyUpdates: i32 = 0
343
+ /** The server's flight is signed and finished, so its Handshake-level output may be sealed. */
344
+ flightReady: boolean = false
242
345
  peerClosed: boolean = false
243
346
  /** The client's TCP stream ended. Without a `close_notify` before it, that is truncation and the connection failed. */
244
347
  ended: boolean = false
@@ -258,16 +361,17 @@ export class TlsRecordServer {
258
361
  this.reader = new TlsRecordReader()
259
362
  this.readProtection = new TlsRecordProtection()
260
363
  this.writeProtection = new TlsRecordProtection()
261
- this.output = new Array<u8>(TLS_RECORD_OUTPUT_SIZE)
262
- this.plain = new Array<u8>(TLS_RECORD_PLAIN_SIZE)
263
- this.scratch = new Array<u8>(TLS_RECORD_SCRATCH_SIZE)
264
- this.none = []
265
- this.readSecret256 = new Array<u8>(SHA256_SIZE)
266
- this.readSecret384 = new Array<u8>(SHA384_SIZE)
267
- this.writeSecret256 = new Array<u8>(SHA256_SIZE)
268
- this.writeSecret384 = new Array<u8>(SHA384_SIZE)
269
- this.readSecret = this.none
270
- this.writeSecret = this.none
364
+ this.empty = []
365
+ this.output = this.empty
366
+ this.plain = this.empty
367
+ this.scratch = this.empty
368
+ // A slot of its own: one buffer of each, until a carrier shares its pools.
369
+ this.pools = new TlsRecordPools(1)
370
+ this.reader.borrowFrom(this.pools.reader)
371
+ this.readTraffic256 = secret(new Array<u8>(SHA256_SIZE))
372
+ this.readTraffic384 = secret(new Array<u8>(SHA384_SIZE))
373
+ this.writeTraffic256 = secret(new Array<u8>(SHA256_SIZE))
374
+ this.writeTraffic384 = secret(new Array<u8>(SHA384_SIZE))
271
375
  this.postHandshake = new Array<u8>(TLS_KEY_UPDATE_SIZE)
272
376
  this.start(tls)
273
377
  }
@@ -275,8 +379,8 @@ export class TlsRecordServer {
275
379
  /**
276
380
  * Starts a new connection in this slot with `tls`, which should be fresh
277
381
  * or just restarted (`TlsServer.restart`, which reuses its buffers):
278
- * every buffer is emptied and the keys of the last connection are wiped;
279
- * the buffers are reused, and nothing is allocated. A
382
+ * every buffer is emptied and given back, zeroed, and the keys of the last
383
+ * connection are wiped; nothing is allocated. A
280
384
  * `tls` that has already failed — a configuration
281
385
  * or randomness `TlsServer` refused — fails this connection at once, with
282
386
  * its alert queued in the clear.
@@ -286,8 +390,6 @@ export class TlsRecordServer {
286
390
  // was used, when it failed or when its slot was closed, and is not
287
391
  // touched here: the array may already hold the next connection's.
288
392
  this.wipeTraffic()
289
- this.readSecret = this.none
290
- this.writeSecret = this.none
291
393
  this.tls = tls
292
394
  this.state = TLS_RECORD_STATE_HANDSHAKE
293
395
  this.alert = 0
@@ -298,8 +400,8 @@ export class TlsRecordServer {
298
400
  this.readLevel = TLS_LEVEL_INITIAL
299
401
  this.writeLevel = TLS_LEVEL_INITIAL
300
402
  this.dropOutput()
301
- this.plainStart = 0
302
- this.plainEnd = 0
403
+ this.dropPlain()
404
+ this.giveBackIdle()
303
405
  this.firstMessageSeen = false
304
406
  this.started = false
305
407
  this.postHandshakeLength = 0
@@ -310,46 +412,172 @@ export class TlsRecordServer {
310
412
  }
311
413
  }
312
414
 
415
+ /**
416
+ * Borrows the record buffers from `pools` from now on, a carrier's set its
417
+ * slots share. Whatever is held from the old pools is given back first,
418
+ * zeroed, and dropped with it, so call it on a slot with nothing in flight:
419
+ * the carrier does, right after the constructor.
420
+ */
421
+ borrowFrom(pools: TlsRecordPools): void {
422
+ this.release()
423
+ this.pools = pools
424
+ this.reader.borrowFrom(pools.reader)
425
+ }
426
+
427
+ /**
428
+ * Gives back every record buffer this slot holds, zeroed, and drops what
429
+ * was in them: unsent output, unread application data and a partial
430
+ * record. The carrier calls it when it closes the slot, after the last
431
+ * flush and `wipeKeys`, so a free slot holds no buffer of the pools'.
432
+ */
433
+ release(): void {
434
+ this.outputStart = 0
435
+ this.outputEnd = 0
436
+ this.dropPlain()
437
+ this.reader.reset()
438
+ this.giveBackIdle()
439
+ }
440
+
441
+ /**
442
+ * Gives back each record buffer that holds nothing still needed, as a
443
+ * connection ends: `output` once everything in it is sent, `plain` once the
444
+ * caller has read it all, and `scratch`. Zeroed with `giveSecret`: every one
445
+ * held plaintext or handshake bytes, and the next connection to borrow it
446
+ * reads zeros. Not called between records, since `giveSecret` zeroes the
447
+ * whole buffer however little of it a record used.
448
+ */
449
+ giveBackIdle(): void {
450
+ if (this.output.length > 0 && this.outputStart === this.outputEnd) {
451
+ this.pools.output.giveSecret(this.output, this.outputUsed)
452
+ this.output = this.empty
453
+ this.outputUsed = 0
454
+ }
455
+ if (this.plain.length > 0 && this.plainStart === this.plainEnd) {
456
+ this.pools.plain.giveSecret(this.plain, this.plainUsed)
457
+ this.plain = this.empty
458
+ this.plainUsed = 0
459
+ }
460
+ if (this.scratch.length > 0) {
461
+ // `open` may write as far as the record's ciphertext: zero all of it.
462
+ this.pools.scratch.giveSecret(this.scratch, TLS_RECORD_SCRATCH_SIZE)
463
+ this.scratch = this.empty
464
+ }
465
+ }
466
+
467
+ /** Forgets the application data the caller has not read. */
468
+ dropPlain(): void {
469
+ this.plainStart = 0
470
+ this.plainEnd = 0
471
+ }
472
+
313
473
  /**
314
474
  * Wipes every key this connection holds: the two application traffic
315
475
  * secrets it keeps for KeyUpdate, both directions' keys, and what is left
316
- * in `TlsServer` (`wipeHandshake`). The carrier calls it when a slot is
476
+ * in `TlsServer` (`TlsServer.wipe`). The carrier calls it when a slot is
317
477
  * closed, so a free slot holds no key.
318
478
  */
319
479
  wipeKeys(): void {
320
- this.wipeTraffic()
321
- this.wipeHandshake()
480
+ this.wipe()
481
+ }
482
+
483
+ /**
484
+ * Wipes everything this slot holds, which is what `wipeKeys` does: both
485
+ * traffic secrets at both hash lengths, both directions' keys, and
486
+ * everything `TlsServer` holds. The record buffers that hold nothing still
487
+ * needed go back too; a failure's alert waiting in `output` stays until it
488
+ * is sent or `release` drops it.
489
+ */
490
+ wipe(): void {
491
+ this.giveBackIdle()
492
+ wipe(this.readTraffic256)
493
+ wipe(this.readTraffic384)
494
+ wipe(this.writeTraffic256)
495
+ wipe(this.writeTraffic384)
496
+ this.readHash = 0
497
+ this.writeHash = 0
498
+ this.readProtection.clear()
499
+ this.writeProtection.clear()
500
+ this.tls.wipe()
322
501
  }
323
502
 
324
503
  /** Wipes the two application traffic secrets and both directions' keys. */
325
504
  wipeTraffic(): void {
326
- // The two secret fields point into these four arrays, or at nothing.
327
- secureZero(this.readSecret256)
328
- secureZero(this.readSecret384)
329
- secureZero(this.writeSecret256)
330
- secureZero(this.writeSecret384)
505
+ this.wipeRead()
506
+ this.wipeWrite()
507
+ }
508
+
509
+ /** Wipes the client's application traffic secret and the read keys. */
510
+ wipeRead(): void {
511
+ // Whichever of the pair held the secret, both are zeroed.
512
+ this.wipeReadSecret()
513
+ this.readHash = 0
331
514
  this.readProtection.clear()
515
+ }
516
+
517
+ /** Wipes the server's application traffic secret and its write keys. */
518
+ wipeWrite(): void {
519
+ this.wipeWriteSecret()
520
+ this.writeHash = 0
332
521
  this.writeProtection.clear()
333
522
  }
334
523
 
524
+ /** Zeroes both of the client's traffic secret fields. */
525
+ wipeReadSecret(): void {
526
+ exposeWith(this.readTraffic256, TLS_RECORD_SERVER_FROM, tlsTrafficZero)
527
+ exposeWith(this.readTraffic384, TLS_RECORD_SERVER_FROM, tlsTrafficZero)
528
+ }
529
+
530
+ /** Zeroes both of the server's traffic secret fields. */
531
+ wipeWriteSecret(): void {
532
+ exposeWith(this.writeTraffic256, TLS_RECORD_SERVER_FROM, tlsTrafficZero)
533
+ exposeWith(this.writeTraffic384, TLS_RECORD_SERVER_FROM, tlsTrafficZero)
534
+ }
535
+
335
536
  /**
336
- * Wipes every secret `TlsServer` still holds — the ephemeral key it was
337
- * handed, the handshake, traffic and exporter secrets and the expected
338
- * Finished — for a connection that has failed or is closed. On the way to
339
- * an open connection each is wiped as soon as it has been used; this is
340
- * for the ways that end early, and for the exporter secret, which a caller
341
- * may read until the connection ends.
537
+ * Copies the client's application traffic secret out of `TlsServer` into
538
+ * the field of its hash's length, wipes the server's copy, and installs the
539
+ * read keys from it. False when the secret is not known or the keys do not
540
+ * install.
342
541
  */
343
- wipeHandshake(): void {
542
+ keepRead(): boolean {
344
543
  const tls: TlsServer = this.tls
345
- secureZero(tls.ephemeralPrivate)
346
- secureZero(tls.handshakeSecret)
347
- secureZero(tls.clientHandshakeSecret)
348
- secureZero(tls.serverHandshakeSecret)
349
- secureZero(tls.clientApplicationSecret)
350
- secureZero(tls.serverApplicationSecret)
351
- secureZero(tls.exporterSecret)
352
- secureZero(tls.expectedClientFinished)
544
+ if (!tls.readKnown(TLS_LEVEL_APPLICATION)) {
545
+ return false
546
+ }
547
+ const pool: TlsSecretPool = tls.secrets()
548
+ this.readHash = tls.hashLength
549
+ let installed: boolean = false
550
+ if (this.readHash === SHA384_SIZE) {
551
+ exposeWith(pool.clientApplication, this.readTraffic384, tlsHeldCopyOver)
552
+ tls.wipeSecret(TLS_SECRET_CLIENT_APPLICATION)
553
+ installed = tlsInstallHeld(this.readProtection, tls.suite, this.readTraffic384)
554
+ } else {
555
+ exposeWith(pool.clientApplication, this.readTraffic256, tlsHeldCopyOver)
556
+ tls.wipeSecret(TLS_SECRET_CLIENT_APPLICATION)
557
+ installed = tlsInstallHeld(this.readProtection, tls.suite, this.readTraffic256)
558
+ }
559
+ return installed
560
+ }
561
+
562
+ /** `keepRead` for the server's application traffic secret and the write keys. */
563
+ keepWrite(): boolean {
564
+ const tls: TlsServer = this.tls
565
+ if (!tls.writeKnown(TLS_LEVEL_APPLICATION)) {
566
+ return false
567
+ }
568
+ const pool: TlsSecretPool = tls.secrets()
569
+ this.writeHash = tls.hashLength
570
+ let installed: boolean = false
571
+ if (this.writeHash === SHA384_SIZE) {
572
+ exposeWith(pool.serverApplication, this.writeTraffic384, tlsHeldCopyOver)
573
+ tls.wipeSecret(TLS_SECRET_SERVER_APPLICATION)
574
+ installed = tlsInstallHeld(this.writeProtection, tls.suite, this.writeTraffic384)
575
+ } else {
576
+ exposeWith(pool.serverApplication, this.writeTraffic256, tlsHeldCopyOver)
577
+ tls.wipeSecret(TLS_SECRET_SERVER_APPLICATION)
578
+ installed = tlsInstallHeld(this.writeProtection, tls.suite, this.writeTraffic256)
579
+ }
580
+ return installed
353
581
  }
354
582
 
355
583
  /** How many bytes `receive` would take now: what the socket should read at most. */
@@ -394,19 +622,21 @@ export class TlsRecordServer {
394
622
  * fails, with nothing more sent, though what is already on `output` may
395
623
  * still go out. Records still waiting for the caller to read, or for the
396
624
  * signature, are handled first, so a `close_notify` among them counts.
625
+ * Every key the connection holds is wiped, as `fail` wipes them.
397
626
  */
398
627
  checkTruncation(): void {
399
628
  if (this.ended && !this.peerClosed && this.state !== TLS_RECORD_STATE_FAILED && this.reader.next() <= 0) {
400
629
  this.state = TLS_RECORD_STATE_FAILED
401
630
  this.dropPending()
402
- this.wipeHandshake()
631
+ this.wipeKeys()
403
632
  }
404
633
  }
405
634
 
406
- /** The socket failed under the connection: it ends with nothing more sent. */
635
+ /** The socket failed under the connection: it ends with nothing more sent, and every key it holds wiped. */
407
636
  abort(): void {
408
637
  this.state = TLS_RECORD_STATE_FAILED
409
638
  this.dropOutput()
639
+ this.wipeKeys()
410
640
  }
411
641
 
412
642
  /** Forgets everything waiting to be sent. */
@@ -416,12 +646,17 @@ export class TlsRecordServer {
416
646
  this.dropPending()
417
647
  }
418
648
 
419
- /** Forgets what is owed but not yet sealed: the handshake queues, the compatibility record, a KeyUpdate and a close. */
649
+ /**
650
+ * Forgets what is owed but not yet sealed: the handshake queues, a
651
+ * NewSessionTicket, the compatibility record, a KeyUpdate and a close.
652
+ */
420
653
  dropPending(): void {
421
654
  this.tls.clearOutput(TLS_LEVEL_INITIAL)
422
655
  this.tls.clearOutput(TLS_LEVEL_HANDSHAKE)
656
+ this.tls.clearOutput(TLS_LEVEL_APPLICATION)
423
657
  this.pendingInitialAt = 0
424
658
  this.pendingHandshakeAt = 0
659
+ this.pendingApplicationAt = 0
425
660
  this.flightReady = false
426
661
  this.ccsDue = false
427
662
  this.keyUpdateDue = false
@@ -437,7 +672,7 @@ export class TlsRecordServer {
437
672
  */
438
673
  process(): void {
439
674
  while (this.state !== TLS_RECORD_STATE_FAILED && !this.peerClosed) {
440
- if (this.tls.state === TLS_STATE_WAIT_SIGNATURE) {
675
+ if (this.tls.state === TLS_STATE_WAIT_SIGNATURE || this.tls.state === TLS_STATE_WAIT_TICKET) {
441
676
  break
442
677
  }
443
678
  const n: i32 = this.reader.next()
@@ -468,7 +703,7 @@ export class TlsRecordServer {
468
703
  * a caller reading a byte at a time does not move the buffer each time.
469
704
  */
470
705
  plainHasRoom(): boolean {
471
- const capacity: i32 = toI32(this.plain.length)
706
+ const capacity: i32 = TLS_RECORD_PLAIN_SIZE
472
707
  if (capacity - (this.plainEnd - this.plainStart) < TLS_MAX_PLAINTEXT) {
473
708
  return false
474
709
  }
@@ -480,6 +715,9 @@ export class TlsRecordServer {
480
715
  this.plainStart = 0
481
716
  this.plainEnd = held
482
717
  }
718
+ if (this.plain.length === 0) {
719
+ this.plain = this.pools.plain.take()
720
+ }
483
721
  return true
484
722
  }
485
723
 
@@ -499,6 +737,9 @@ export class TlsRecordServer {
499
737
  if (toI32(buf[off]) === TLS_CONTENT_CHANGE_CIPHER_SPEC) {
500
738
  return this.handleChangeCipherSpec(buf, off, n - TLS_RECORD_HEADER_SIZE)
501
739
  }
740
+ if (this.scratch.length === 0) {
741
+ this.scratch = this.pools.scratch.take()
742
+ }
502
743
  const length: i32 = this.readProtection.open(buf, off, n, this.scratch, TLS_RECORD_SERVER_FROM)
503
744
  if (length < 0) {
504
745
  return -length
@@ -575,6 +816,9 @@ export class TlsRecordServer {
575
816
  this.plain[this.plainEnd + k] = this.scratch[k]
576
817
  }
577
818
  this.plainEnd = this.plainEnd + length
819
+ if (this.plainEnd > this.plainUsed) {
820
+ this.plainUsed = this.plainEnd
821
+ }
578
822
  this.idleRecords = 0
579
823
  return 0
580
824
  }
@@ -600,7 +844,10 @@ export class TlsRecordServer {
600
844
  }
601
845
  const description: i32 = toI32(this.scratch[1])
602
846
  if (description === TLS_ALERT_CLOSE_NOTIFY) {
847
+ // The client writes nothing after it (§6.1), and nothing after it is
848
+ // read, so the read keys have opened their last record.
603
849
  this.peerClosed = true
850
+ this.wipeRead()
604
851
  return 0
605
852
  }
606
853
  if (description === TLS_ALERT_USER_CANCELED) {
@@ -609,6 +856,9 @@ export class TlsRecordServer {
609
856
  this.peerAlert = description
610
857
  this.state = TLS_RECORD_STATE_FAILED
611
858
  this.dropOutput()
859
+ // The client's error alert ends the connection as `fail` does, with
860
+ // nothing sent back, so every key it held is wiped now.
861
+ this.wipeKeys()
612
862
  return 0
613
863
  }
614
864
 
@@ -667,11 +917,15 @@ export class TlsRecordServer {
667
917
  * The client's KeyUpdate: its next records are under the next secret
668
918
  * (§7.2), the old one wiped. When it asked for an update the server owes
669
919
  * one of its own before its next application data, which `pump` sends.
670
- * Answers 0, or `internal_error` when the keys do not install, which only
671
- * a secret of the wrong length could cause.
920
+ * Answers 0, or `internal_error` when no read secret is kept (`readHash`
921
+ * 0) or the keys do not install.
672
922
  */
673
923
  updateReadKeys(requested: boolean): i32 {
674
- if (!this.readProtection.advance(this.readSecret)) {
924
+ const advanced: boolean =
925
+ this.readHash === SHA384_SIZE
926
+ ? tlsAdvanceHeld(this.readProtection, this.readTraffic384)
927
+ : this.readHash !== 0 && tlsAdvanceHeld(this.readProtection, this.readTraffic256)
928
+ if (!advanced) {
675
929
  return TLS_ALERT_INTERNAL_ERROR
676
930
  }
677
931
  if (requested) {
@@ -700,34 +954,28 @@ export class TlsRecordServer {
700
954
  toI32(tls.outputInitial[TLS_SESSION_ID_ECHO_AT]) > 0
701
955
  }
702
956
  if (this.readLevel === TLS_LEVEL_INITIAL) {
703
- const handshake: u8[] | null = tls.readSecret(TLS_LEVEL_HANDSHAKE)
704
- if (handshake !== null) {
705
- this.readProtection.install(tls.suite, handshake)
957
+ if (tls.readKnown(TLS_LEVEL_HANDSHAKE)) {
958
+ tlsInstallHeld(this.readProtection, tls.suite, tls.secrets().clientHandshake)
706
959
  this.readLevel = TLS_LEVEL_HANDSHAKE
707
960
  }
708
961
  }
709
- if (tls.state !== TLS_STATE_WAIT_CLIENT_HELLO) {
710
- // The ServerHello is written, so the exchange is done with the key.
711
- secureZero(tls.ephemeralPrivate)
962
+ if (tls.state !== TLS_STATE_WAIT_CLIENT_HELLO && tls.state !== TLS_STATE_WAIT_TICKET) {
963
+ // The ServerHello is written, so the exchange is done with the key. A
964
+ // hello that offers a ticket waits for `resume` before any ServerHello,
965
+ // and the exchange still needs the key there.
966
+ tls.wipeEphemeral()
712
967
  }
713
968
  if (tls.state === TLS_STATE_WAIT_FINISHED || tls.state === TLS_STATE_CONNECTED) {
714
969
  this.flightReady = true
715
970
  // The flight is signed and finished: the handshake secret and the
716
971
  // client's handshake secret have done their work in `TlsServer`, and
717
972
  // the read keys made from the latter are installed.
718
- secureZero(tls.handshakeSecret)
719
- secureZero(tls.clientHandshakeSecret)
973
+ tls.wipeSecret(TLS_SECRET_HANDSHAKE)
974
+ tls.wipeSecret(TLS_SECRET_CLIENT_HANDSHAKE)
720
975
  }
721
976
  if (tls.state === TLS_STATE_CONNECTED && this.readLevel === TLS_LEVEL_HANDSHAKE) {
722
- this.readSecret = tlsKeepSecret(
723
- tls.readSecret(TLS_LEVEL_APPLICATION),
724
- this.readSecret256,
725
- this.readSecret384,
726
- this.none
727
- )
728
- secureZero(tls.clientApplicationSecret)
729
977
  // A direction whose keys did not install would read in the clear.
730
- if (!this.readProtection.install(tls.suite, this.readSecret)) {
978
+ if (!this.keepRead()) {
731
979
  this.fail(TLS_ALERT_INTERNAL_ERROR)
732
980
  return
733
981
  }
@@ -735,13 +983,25 @@ export class TlsRecordServer {
735
983
  if (this.state === TLS_RECORD_STATE_HANDSHAKE) {
736
984
  this.state = TLS_RECORD_STATE_OPEN
737
985
  }
986
+ // With tickets on, a connection no ticket will be issued to has no use
987
+ // for the resumption master secret: wiped at once, not at close.
988
+ if (tls.tickets && !tls.ticketWanted) {
989
+ tls.wipeResumption()
990
+ }
738
991
  }
739
992
  this.pump()
740
993
  }
741
994
 
742
- /** Whether `need` more bytes fit in `output`, moving what is unsent to the front first. */
995
+ /**
996
+ * Whether `need` more bytes fit in `output`, moving what is unsent to the
997
+ * front first. Borrows `output` when none is held, since a record is about
998
+ * to be sealed into it.
999
+ */
743
1000
  outputHasRoom(need: i32): boolean {
744
- const capacity: i32 = toI32(this.output.length)
1001
+ if (this.output.length === 0) {
1002
+ this.output = this.pools.output.take()
1003
+ }
1004
+ const capacity: i32 = TLS_RECORD_OUTPUT_SIZE
745
1005
  if (this.outputEnd + need > capacity && this.outputStart > 0) {
746
1006
  const held: i32 = this.outputEnd - this.outputStart
747
1007
  for (let k: i32 = 0; k < held; k++) {
@@ -783,9 +1043,14 @@ export class TlsRecordServer {
783
1043
  * mistake here could cause, fails the connection.
784
1044
  */
785
1045
  sealRecord(type: i32, data: u8[], off: i32, len: i32): boolean {
786
- if (!this.outputHasRoom(this.recordRoom(len))) {
1046
+ const room: i32 = this.recordRoom(len)
1047
+ if (!this.outputHasRoom(room)) {
787
1048
  return false
788
1049
  }
1050
+ // Counted before the seal, so a refused seal's partial write is wiped when the buffer goes back.
1051
+ if (this.outputEnd + room > this.outputUsed) {
1052
+ this.outputUsed = this.outputEnd + room
1053
+ }
789
1054
  const n: i32 = this.writeProtection.seal(
790
1055
  type,
791
1056
  data,
@@ -840,9 +1105,7 @@ export class TlsRecordServer {
840
1105
  }
841
1106
  // After its close_notify the server sends nothing more (§6.1).
842
1107
  if (this.state === TLS_RECORD_STATE_CLOSED) {
843
- if (this.closeDue && this.sendAlert(TLS_ALERT_LEVEL_WARNING, TLS_ALERT_CLOSE_NOTIFY)) {
844
- this.closeDue = false
845
- }
1108
+ this.sealClose()
846
1109
  return
847
1110
  }
848
1111
  const tls: TlsServer = this.tls
@@ -873,9 +1136,8 @@ export class TlsRecordServer {
873
1136
  // in as soon as the ServerHello is sealed. `install` refuses only a suite
874
1137
  // or a secret `TlsServer` never hands over; the check keeps the flight
875
1138
  // from ever being sealed in the clear.
876
- const handshake: u8[] | null = tls.writeSecret(TLS_LEVEL_HANDSHAKE)
877
- if (this.writeLevel === TLS_LEVEL_INITIAL && handshake !== null) {
878
- if (!this.writeProtection.install(tls.suite, handshake)) {
1139
+ if (this.writeLevel === TLS_LEVEL_INITIAL && tls.writeKnown(TLS_LEVEL_HANDSHAKE)) {
1140
+ if (!tlsInstallHeld(this.writeProtection, tls.suite, tls.secrets().serverHandshake)) {
879
1141
  this.fail(TLS_ALERT_INTERNAL_ERROR)
880
1142
  return
881
1143
  }
@@ -897,21 +1159,28 @@ export class TlsRecordServer {
897
1159
  this.writeLevel === TLS_LEVEL_HANDSHAKE &&
898
1160
  (tls.state === TLS_STATE_WAIT_FINISHED || tls.state === TLS_STATE_CONNECTED)
899
1161
  ) {
900
- this.writeSecret = tlsKeepSecret(
901
- tls.writeSecret(TLS_LEVEL_APPLICATION),
902
- this.writeSecret256,
903
- this.writeSecret384,
904
- this.none
905
- )
906
- secureZero(tls.serverApplicationSecret)
907
- secureZero(tls.serverHandshakeSecret)
1162
+ tls.wipeSecret(TLS_SECRET_SERVER_HANDSHAKE)
908
1163
  // A direction whose keys did not install would write in the clear.
909
- if (!this.writeProtection.install(tls.suite, this.writeSecret)) {
1164
+ if (!this.keepWrite()) {
910
1165
  this.fail(TLS_ALERT_INTERNAL_ERROR)
911
1166
  return
912
1167
  }
913
1168
  this.writeLevel = TLS_LEVEL_APPLICATION
914
1169
  }
1170
+ // A NewSessionTicket is a post-handshake message, under the application
1171
+ // keys (§4.6.1).
1172
+ if (this.writeLevel === TLS_LEVEL_APPLICATION && tls.outputApplicationLength > 0) {
1173
+ this.pendingApplicationAt = this.sealQueue(
1174
+ tls.outputApplication,
1175
+ this.pendingApplicationAt,
1176
+ tls.outputApplicationLength
1177
+ )
1178
+ if (this.pendingApplicationAt < tls.outputApplicationLength) {
1179
+ return
1180
+ }
1181
+ tls.clearOutput(TLS_LEVEL_APPLICATION)
1182
+ this.pendingApplicationAt = 0
1183
+ }
915
1184
  if (this.keyUpdateDue && this.writeLevel === TLS_LEVEL_APPLICATION && this.sendKeyUpdate(false)) {
916
1185
  this.keyUpdateDue = false
917
1186
  }
@@ -927,7 +1196,11 @@ export class TlsRecordServer {
927
1196
  if (!this.sealRecord(TLS_CONTENT_HANDSHAKE, message, TLS_RECORD_SERVER_FROM, TLS_KEY_UPDATE_SIZE)) {
928
1197
  return false
929
1198
  }
930
- if (!this.writeProtection.advance(this.writeSecret)) {
1199
+ const advanced: boolean =
1200
+ this.writeHash === SHA384_SIZE
1201
+ ? tlsAdvanceHeld(this.writeProtection, this.writeTraffic384)
1202
+ : this.writeHash !== 0 && tlsAdvanceHeld(this.writeProtection, this.writeTraffic256)
1203
+ if (!advanced) {
931
1204
  this.fail(TLS_ALERT_INTERNAL_ERROR)
932
1205
  return false
933
1206
  }
@@ -958,7 +1231,9 @@ export class TlsRecordServer {
958
1231
  /**
959
1232
  * Fails the connection with `alert`, sent as a fatal alert under whatever
960
1233
  * the server writes with now, in the clear before its first keys. What
961
- * was still owed is dropped: the alert is the last thing the server sends.
1234
+ * was still owed is dropped: the alert is the last thing the server sends,
1235
+ * so once it is sealed every key the connection holds is wiped, the
1236
+ * traffic keys and secrets as well as what `TlsServer` holds.
962
1237
  */
963
1238
  fail(alert: i32): void {
964
1239
  if (this.state === TLS_RECORD_STATE_FAILED) {
@@ -966,7 +1241,6 @@ export class TlsRecordServer {
966
1241
  }
967
1242
  const closed: boolean = this.state === TLS_RECORD_STATE_CLOSED
968
1243
  this.dropPending()
969
- this.wipeHandshake()
970
1244
  // Failed first, so a seal that refuses the alert cannot come back here.
971
1245
  this.state = TLS_RECORD_STATE_FAILED
972
1246
  this.alert = alert
@@ -974,6 +1248,9 @@ export class TlsRecordServer {
974
1248
  if (!closed) {
975
1249
  this.sendAlert(TLS_ALERT_LEVEL_FATAL, alert)
976
1250
  }
1251
+ // After the branch, so that the alert is sealed under the write keys
1252
+ // before they go, and so that `opt` keeps one wipe rather than merging two.
1253
+ this.wipeKeys()
977
1254
  }
978
1255
 
979
1256
  /** Seals an alert record of `level` and `description`; false, sending nothing, when `output` has no room for it. */
@@ -1039,7 +1316,7 @@ export class TlsRecordServer {
1039
1316
  let chunk: i32 = left < most ? left : most
1040
1317
  // A record shortened to the room there is, rather than none at all.
1041
1318
  this.outputHasRoom(this.recordRoom(chunk))
1042
- const room: i32 = toI32(this.output.length) - this.outputEnd - this.recordRoom(TLS_RECORD_SERVER_FROM)
1319
+ const room: i32 = TLS_RECORD_OUTPUT_SIZE - this.outputEnd - this.recordRoom(TLS_RECORD_SERVER_FROM)
1043
1320
  if (room < chunk) {
1044
1321
  chunk = room
1045
1322
  }
@@ -1094,6 +1371,8 @@ export class TlsRecordServer {
1094
1371
  * answers `TLS_RECORD_PIPE` and nothing more is sent. What was still owed
1095
1372
  * of the handshake is dropped; application data already written stays
1096
1373
  * ahead of the alert, which waits for room in `output` when there is none.
1374
+ * The resumption master secret is wiped, since nothing issues a ticket
1375
+ * from it, and the write keys once the `close_notify` is sealed (`sealClose`).
1097
1376
  * Nothing happens once the connection has failed or closed.
1098
1377
  */
1099
1378
  close(): void {
@@ -1101,9 +1380,23 @@ export class TlsRecordServer {
1101
1380
  return
1102
1381
  }
1103
1382
  this.dropPending()
1383
+ this.tls.wipeResumption()
1104
1384
  this.state = TLS_RECORD_STATE_CLOSED
1105
1385
  this.closeDue = true
1106
- this.pump()
1386
+ this.sealClose()
1387
+ }
1388
+
1389
+ /**
1390
+ * Seals the `close_notify` `close` owes, once `output` has room for it. It
1391
+ * is the last record the server writes (§6.1), so the write keys and the
1392
+ * server's traffic secret are wiped as soon as it is sealed. The read keys
1393
+ * stay: the client may still send until its own `close_notify`.
1394
+ */
1395
+ sealClose(): void {
1396
+ if (this.closeDue && this.sendAlert(TLS_ALERT_LEVEL_WARNING, TLS_ALERT_CLOSE_NOTIFY)) {
1397
+ this.closeDue = false
1398
+ this.wipeWrite()
1399
+ }
1107
1400
  }
1108
1401
 
1109
1402
  /** What the CertificateVerify signs, while `interest` says `TLS_RECORD_SIGN`; `null` otherwise. */
@@ -1134,12 +1427,55 @@ export class TlsRecordServer {
1134
1427
  return this.state === TLS_RECORD_STATE_FAILED ? this.alert : 0
1135
1428
  }
1136
1429
 
1430
+ /**
1431
+ * While `interest` says `TLS_RECORD_TICKET` in the handshake: reads the
1432
+ * ClientHello's tickets under the keys the carrier lends (`TlsServer.resume`)
1433
+ * and seals the flight that answers it, resumed or in full. Answers 0, or
1434
+ * the alert the connection failed with — `internal_error` when no ticket
1435
+ * waits.
1436
+ */
1437
+ resume(
1438
+ current: Secret<u8[]>,
1439
+ currentInfo: TlsTicketKeyInfo,
1440
+ previous: Secret<u8[]>,
1441
+ previousInfo: TlsTicketKeyInfo,
1442
+ strikes: TlsTicketStrikes,
1443
+ now: i64
1444
+ ): i32 {
1445
+ if (this.state !== TLS_RECORD_STATE_HANDSHAKE) {
1446
+ return this.state === TLS_RECORD_STATE_FAILED ? this.alert : TLS_ALERT_INTERNAL_ERROR
1447
+ }
1448
+ const alert: i32 = this.tls.resume(current, currentInfo, previous, previousInfo, strikes, now)
1449
+ if (alert !== 0) {
1450
+ this.fail(alert)
1451
+ return alert
1452
+ }
1453
+ this.advance()
1454
+ this.process()
1455
+ return this.state === TLS_RECORD_STATE_FAILED ? this.alert : 0
1456
+ }
1457
+
1458
+ /**
1459
+ * While `interest` says `TLS_RECORD_TICKET` on an open connection: seals a
1460
+ * ticket under `key` (`TlsServer.issue`) and queues its NewSessionTicket.
1461
+ * Whether one was queued; either way the resumption master secret is
1462
+ * wiped, and no second ticket is issued.
1463
+ */
1464
+ issue(key: Secret<u8[]>, info: TlsTicketKeyInfo, now: i64): boolean {
1465
+ if (this.state !== TLS_RECORD_STATE_OPEN) {
1466
+ return false
1467
+ }
1468
+ const written: boolean = this.tls.issue(key, info, now)
1469
+ this.pump()
1470
+ return written
1471
+ }
1472
+
1137
1473
  /**
1138
1474
  * What the connection wants next, as `TLS_RECORD_*` bits: read the socket
1139
1475
  * (there is room, and the stream has not ended), write it (`output` holds
1140
1476
  * bytes), `read` (application data, or the end of the client's stream),
1141
- * sign, or nothing more ever (`TLS_RECORD_DONE`: failed or closed, and
1142
- * everything is sent).
1477
+ * sign, handle a ticket, or nothing more ever (`TLS_RECORD_DONE`: failed
1478
+ * or closed, and everything is sent).
1143
1479
  */
1144
1480
  interest(): i32 {
1145
1481
  let bits: i32 = 0
@@ -1168,6 +1504,16 @@ export class TlsRecordServer {
1168
1504
  if (this.state === TLS_RECORD_STATE_HANDSHAKE && this.tls.state === TLS_STATE_WAIT_SIGNATURE) {
1169
1505
  bits = bits | TLS_RECORD_SIGN
1170
1506
  }
1507
+ const ticketHello: boolean =
1508
+ this.state === TLS_RECORD_STATE_HANDSHAKE && this.tls.state === TLS_STATE_WAIT_TICKET
1509
+ const ticketOwed: boolean =
1510
+ this.state === TLS_RECORD_STATE_OPEN &&
1511
+ this.tls.state === TLS_STATE_CONNECTED &&
1512
+ this.tls.ticketWanted &&
1513
+ !this.tls.issued
1514
+ if (ticketHello || ticketOwed) {
1515
+ bits = bits | TLS_RECORD_TICKET
1516
+ }
1171
1517
  return bits
1172
1518
  }
1173
1519
  }