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