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