@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.
- package/INSTALL.md +43 -25
- package/bin/nish +0 -0
- package/package.json +2 -2
- package/runtime/nish.d.ts +55 -5
- package/runtime/nish.h +231 -20
- package/runtime/nish.mjs +16 -0
- package/runtime/runtime-aes.c +490 -0
- package/runtime/runtime-cross.c +366 -0
- package/runtime/runtime-host.c +7 -6
- package/runtime/runtime-net.c +93 -34
- package/runtime/runtime-os.c +213 -15
- package/runtime/runtime-parallel.c +590 -107
- package/runtime/runtime-simd.c +203 -0
- package/runtime/runtime-wasm.c +16 -3
- package/runtime/runtime.c +89 -23
- package/runtime/shim.mjs +71 -1
- package/scripts/build.sh +225 -20
- package/std/README.md +86 -55
- package/std/bytes.ts +163 -0
- package/std/crypto/LICENSE-libsecp256k1 +19 -0
- package/std/crypto/aes.ts +238 -76
- package/std/crypto/chacha20poly1305.ts +43 -9
- package/std/crypto/ed25519.ts +840 -0
- package/std/crypto/hkdf.ts +304 -12
- package/std/crypto/hmac.ts +245 -53
- package/std/crypto/p256.ts +1376 -42
- package/std/crypto/x25519.ts +55 -37
- package/std/crypto/x509-random.ts +71 -0
- package/std/crypto/x509.ts +9 -9
- package/std/json-tokenizer.ts +1296 -0
- package/std/json-typed.ts +1014 -0
- package/std/json-value.ts +1163 -0
- package/std/json.ts +1412 -70
- package/std/net/buffer-pool.ts +151 -0
- package/std/net/hpack.ts +416 -112
- package/std/net/http-fields.ts +93 -0
- package/std/net/http1-server.ts +417 -55
- package/std/net/http1.ts +31 -6
- package/std/net/http2-tls.ts +112 -24
- package/std/net/http2.ts +333 -75
- package/std/net/http3-server.ts +231 -45
- package/std/net/http3.ts +187 -28
- package/std/net/prefork.ts +412 -0
- package/std/net/quic-conn-params.ts +86 -16
- package/std/net/quic-listener.ts +576 -219
- package/std/net/quic-packet.ts +443 -58
- package/std/net/quic-recovery.ts +361 -30
- package/std/net/quic-stream.ts +434 -28
- package/std/net/quic.ts +922 -206
- package/std/net/router.ts +676 -0
- package/std/net/tls/codec.ts +426 -27
- package/std/net/tls/record-server.ts +478 -132
- package/std/net/tls/record.ts +133 -42
- package/std/net/tls/schedule.ts +464 -55
- package/std/net/tls/ticket.ts +610 -0
- package/std/net/tls-tcp.ts +204 -57
- package/std/net/tls.ts +1132 -305
- package/std/net/url.ts +571 -0
- package/std/random.ts +94 -0
- package/std/text.ts +77 -0
- 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
|
-
* **
|
|
36
|
-
* application data waiting for the caller each live in
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* connection with `
|
|
43
|
-
*
|
|
44
|
-
*
|
|
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
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
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
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
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
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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
|
-
/**
|
|
236
|
-
|
|
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.
|
|
262
|
-
this.
|
|
263
|
-
this.
|
|
264
|
-
this.
|
|
265
|
-
|
|
266
|
-
this.
|
|
267
|
-
this.
|
|
268
|
-
this.
|
|
269
|
-
this.
|
|
270
|
-
this.
|
|
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
|
|
279
|
-
*
|
|
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.
|
|
302
|
-
this.
|
|
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` (`
|
|
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.
|
|
321
|
-
|
|
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
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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
|
-
*
|
|
337
|
-
*
|
|
338
|
-
*
|
|
339
|
-
*
|
|
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
|
-
|
|
542
|
+
keepRead(): boolean {
|
|
344
543
|
const tls: TlsServer = this.tls
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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.
|
|
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
|
-
/**
|
|
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 =
|
|
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
|
|
671
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
704
|
-
|
|
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
|
-
|
|
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
|
-
|
|
719
|
-
|
|
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.
|
|
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
|
-
/**
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
877
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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 =
|
|
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.
|
|
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
|
|
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
|
}
|